c++pluginsdefine-syntax

Why #define variable in library is overridden from #define in calling application?


I am trying to a make plugin system which will have a header file for all plugins to include. In that header the version of the plugin system is defined in a #define like so:

PluginHeader.hpp:

#define PLUGIN_SYSTEM_VERSION "00.001"

class PluginSystem
{
public:
    string GetSystemVersion(){return PLUGIN_SYSTEM_VERSION; }
    void MyPluginDoStuff(){...}
}

I compile my plugin with this header in a dll and export it. Afterwards, i import it in the main application and i change the value of the #define in the calling application to be "00.002". If i call the method GetSystemVersion in the dll, then i am getting the new value "00.002".

Is this the expected behavior? How can i make the dll keep its defined value?


Solution

  • It is neither expected nor unexpected behaviour. Your program has undefined behaviour, so any outcome (whether it seems "right" or not) is possible.

    Your member function PluginSystem::GetSystemVersion() is defined (implemented) within its class definition, so is implicitly inline.

    The problem is, by having different definitions of the macro PLUGIN_SYSTEM_VERSION in different compilation units (e.g. in your DLL and in a separate source file that includes your header), you cause your program to have multiple definitions of PluginSystem::GetSystemVersion() (and, in fact, the whole class PluginSystem) in your program. If those definitions aren't exactly the same, you're breaking the one-definition rule. Which for an inlined function (and for a class) means that all definitions seen by different source files must be identical.

    Breaking the one-definition rule means your program has undefined behaviour. Seeing different versions of an inline function being called in different compilation units (e.g. the DLL version called in some situations but not others) is one possible symptom of that.

    If you want to have the function in the DLL, then

    1. Change the class definition so that the function is not inline within the class definition, and ensure that class definition is used consistently everywhere (both in all source files for your program, and in all source files for the DLL) - for example, by including the same header everywhere it is needed;
    2. In exactly one source file, for the DLL, include exactly ONE definition of the function outside the class definition.

    Although not technically necessary, I'd encourage you to avoid using macros (other than include guards) in the header (or, even, using a macro for it at all - there are alternatives). If you must have a macro PLUGIN_SYSTEM_VERSION, define it locally to the single source file that defines PluginSystem::GetSystemVersion(). That avoids the possibility of confusing yourself (and increasing chances of getting unexpected behaviours) by having different incantations of PLUGIN_SYSTEM_VERSION (and code affected by it) in different parts of your program.