I was trying to use #ifdef preprocessor around virtual functions. A simplified version of code looks like following:
class Base
{
#ifdef ENABLE_FLAG
virtual void function1();
#endif //ENABLE_FLAG
virtual void function2();
virtual void function3();
};
class Child : public Base
{
#ifdef ENABLE_FLAG
void function1() override;
#endif //ENABLE_FLAG
void function2() override;
void function3() override;
};
The code compiles fine. However, when my application is calling Child::function3(), it actually ended up calling Child::function2() for some reason. I think the preprocessor has messed up the virtual table some how.
I am running debug mode in visual studio 2017. I'm curious what is the cause of this runtime problem. Is this a compiler dependent behaviour?
Another interesting thing to note is that if I make sure ENABLE_FLAG is defined and remove the #ifdef clause in the Child class and keep the one in Base class, the compiler is actually throwing a compilation error. What difference does it make here?
UPDATE: This class is used both in a main program and in a library.
Virtual functions are listed in a per-class table - the virtual function table
or vtable
- each instance of the class contains a pointer to this table which itself contains pointers to each of the virtual functions for that class, in the order in which they are listed in the class declaration (*). (There are plenty of tutorial articles and videos that describe virtual tables if you want to look.)
So if you compile part of your program with that #ifdef
enabled and another part of your program with that #ifdef
not enabled - there will be a different number of virtual functions that the different parts see, and the vtables will be different. Which is not supposed to happen, and leads to the problem you're seeing.
So don't do that. You're violating a very strict rule of C++ called ODR
or "One Definition Rule". Everything is up in the air if you do that - i.e., anything and everything can go wrong.
(Odd thing about the ODR: for such a very strict crucial rule, the compiler (i.e., compilation system as a whole) is not required in any way to tell you you've violated it. So, really, don't.
By the way, all libraries and the main program must see the same class declaration! That stuff above about the ODR? The C++ standard doesn't know about "libraries" - static, dynamic, or otherwise. For it there are only programs and compilation units. (These "library" things are just a convenience the compilation systems provide for us.) So all the rules apply across entire programs - and the ODR is the one that will most often bite you here! (As you have just discovered...)
(*) To a first approximation, not including multiple inheritance ...