Assume the following code:
/******* mylib.h *******/
#include <iostream>
class foo
{
public:
void test()
{
#ifdef MYDEF
::std::cout<<"ZERO\n";
#else
::std::cout<<"ONE\n";
#endif
}
};
/******* module.h *******/
#include "mylib.h"
class module
{
public:
void test();
}
/******* module.cpp *******/
#include "module.h"
class module
{
public:
void test()
{
foo foo_;
foo_.test();
}
};
/******* main.cpp *******/
#define MYDEF
#include "mylib.h"
#include "module.h"
void main()
{
foo foo_;
module mod_;
foo_.test();
mod_.test();
}
Using g++, I compile each implementation file independently:
module.cpp –> module.o
main.cpp –> main.o
Then I proceed to link them together, creating the a.out executable:
module.o + main.o = a.out
All the above works out just fine, and executing a.out prints out:
ZERO
ZERO
Questions:
Considering that module.o and main.o must have a different definition of foo::test() due to main.cpp defining MYDEF and module.cpp not defining MYDEF, the linker must then choose one of the two definitions to keep because, as we see above, all calls to foo::test() express the same behaviour. How does the linker decide which foo::test() definition to use? Can I influence this decision? What if my library has a dozen more functions all depending on the MYDEF macro, is it a guarantee that all these functions will see the same MYDEF macro definition status? Making use of such behaviour breaks the one definition rule, but how else should one go about implementing for example conditionally compiled debug prints in foo::test() in a better way?
>Solution :
It is impossible to influence this reliably.
Having two translation units compiled with MYDEF set differently is a ODR violation, because your program then contains two definitions for the class foo which are not identical. The program then is IFNDR (ill-formed, no diagnostic required) and effectively has undefined behavior.
This is not only a theoretical concern either. The compiler may arbitrarily inline calls to the test function or compile other functions under the assumption that test has the same behavior as visible to it in the translation unit.
So even if you could influence the linker to choose one of the two implementations, the program may still not behave as you would expect.
You get the conditional behavior reliably by either always recompiling the whole program with the new macro setting if you change it or by not using a header-only library for it. Instead put the conditional code in a single .cpp containing the definition of the method.