jmetcalfe
jmetcalfe

Reputation: 1316

Generating an error on conflicting definitions with inline linkage

I came across a curious bug today which I had somehow managed to avoid up until now.

file1.cpp:

#include <iostream>
inline void print() { std::cout << "Print1\n"; }
void y() { print(); }

file2.cpp:

#include <iostream>
inline void print() { std::cout << "Print2\n"; }
void x() { print(); }

main.cpp:

int x();
int y();

int main(){
    x();
    y();
}

Output:

Print1                         (Expected Print2)
Print1

Because print() has inline linkage, this produces no multiple definition error (compiled with g++ -Wall file1.cpp file2.cpp main.cpp) and the duplicate symbol is silently collapsed. The actual case where I saw this was with inline class methods, not explicit inline functions, but the effect is the same.

I am wondering if there is a linker option or similar which will allow me to produce a warning when this type of error is made?

Upvotes: 7

Views: 519

Answers (4)

Mark B
Mark B

Reputation: 96251

The functions are not internal linkage or in an anonymous namespace, so they're visibly external with the same name. They have different bodies so you've clearly violated the one definition rule. At that point any speculation as to what will happen isn't useful as your program is malformed.

I'm guessing that you compiled without optimization and the compiler generated function calls instead of actually inlining, and it picked one of the bodies to use as the function to call (leaving the other one orphaned). Now presumably if you compiled with optimization on, your expected output would be emitted but your program would still be incorrect.

EDIT for comment: Unfortunately it's not required for compilers to diagnose violations of the one definition rule (and they may not even be able to detect all cases). However there are a few things you can do:

  • Make source-private functions always either static or in an anonymous namespace (I prefer the namespace for logical grouping purposes but either is fine).
  • Pay special attention to inline methods (regardless of location) as they tell the compiler explicitly that all versions will be identical (for non-inline methods you'll most likely at least get a duplicate symbol error from the linker). The safest approach here is to avoid global-namespace inline functions (or take particular care in your naming). Also you need to be really careful that changed #defines don't change the function body (for example using assert in an inline function where one use has NDEBUG and the other doesn't).
  • Use classes and namespaces logically to break up code and help prevent multiple definitions of the same symbol.

Upvotes: 3

abdul
abdul

Reputation: 134

To make a function local to a .cpp (or .c) file you have to make that function as static. When you declared print in two separate .cpp files both of them where compiled to print_v, The second symbol was dropped(ignored)* by the compiler.

This is just an assumption of how it acted on my machine. I tried the same code on a MAC OS X mountain Lion and I got the same result as you, but I was able to solve it by adding static to both functions.

* if you change the order of the files in the compiling, the behavior would change. try g++ file{2,1}.cpp main.cpp and the output should be

print2

print2

Sincerely, Abdulrahman Alotaibi

Upvotes: -2

Anirudh Ramanathan
Anirudh Ramanathan

Reputation: 46738

It isn't necessary/mandated that an error/warning be generated, by the standard. It is a violation of C++'s One Definition Rule, because of their having different function-bodies.

Quoting from: Extern Inlines by Default

The meaning of "extern inline" is that if calls to a function are not generated inline, then a compiler should make just one copy of the definition of the function, to be shared across all object files.

If the above occurs, that program's behavior is considered undefined according to the language standard, but that neither the compiler nor the linker is required to give a diagnostic message. In practice, this means that, depending on how the implementation works, the compiler or linker may just silently pick one of the definitions to be used everywhere.

The behaviour is consistent with GCC's definition of vague linkage here.

Upvotes: 2

Andrew White
Andrew White

Reputation: 53506

I am going to shamelessness copy-paste from an email thread I found here on a similar topic...

This is not a gcc issue.

You didn't mention which OS you are using. I will assume it is GNU/Linux. On GNU/Linux, or any other ELF based system, there is a single global namespace of global variables and functions. When two shared libraries use the same global variable name, they are referring to the same variable. This is a feature.

If you want something different to happen, look into symbol visibility and linker version scripts.

It would seem that you should be able to tell the linker/compiler that symbol X should be unique and complain if it isn't though.

Upvotes: 0

Related Questions