Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating an error on conflicting definitions with inline linkage

Tags:

c++

inline

linker

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?

like image 985
jmetcalfe Avatar asked Feb 22 '13 19:02

jmetcalfe


2 Answers

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.

like image 143
Anirudh Ramanathan Avatar answered Nov 12 '22 22:11

Anirudh Ramanathan


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.
like image 37
Mark B Avatar answered Nov 12 '22 22:11

Mark B