Incorrect behavior with a DLL created using CMake and curiously repeating patterns (C ++)

I ran into a confusing problem with DLL files created by CMake on Windows. In my library, I use the Curiously Recurring Template Pattern to give certain classes a unique ID:

// da/Attribute.h: #ifndef DA_ATTRIBUTE_H #define DA_ATTRIBUTE_H namespace da { typedef unsigned int AttributeId; class AttributeBase { public: virtual AttributeId getTypeId() const=0; protected: /** Static ID counter. Every class that derives da::AttributeBase is assigned an increment of this counter as its type ID number */ static AttributeId sNextId; }; template <class Derived> class Attribute : public AttributeBase { private: static AttributeId msTypeId; public: Attribute() { if (msTypeId == 0) { msTypeId = ++sNextId; } } virtual ~Attribute() { } /** For static contexts */ static AttributeId typeId() { if (msTypeId == 0) { msTypeId = ++sNextId; } return msTypeId; } AttributeId getTypeId() const { return typeId(); } }; template <class Derived> AttributeId Attribute<Derived>::msTypeId = 0; } #endif 

The problem is that when I associate a DLL with an executable project, there are some inconsistencies with the various ID methods. For instance:

 // Foo.h struct Foo : public da::Attribute<Foo> { Foo() { } }; 

...

 // main.cpp Foo *foo = new Foo; Foo->getTypeId() == 1 // True Foo::typeId() == 1 // Should be true, but isn't. Foo::typeId() == 2 

Starting with GDB with a break in Foo :: getTypeID (), I found that "msTypeId" and "Foo :: msTypeId" have different memory addresses. What the hell. p>

This only happens when Foo is defined in the DLL. (And only on Windows 7, apparently, I do not have this problem in my Debian build). If I create a derived class inside main.cpp or just compile all the code from the library into an executable file, skipping the DLL completely, it works without problems.

Everything was compiled using MSYS and MinGW, with GCC 4.7 in Windows 7 Home Premium.

Here's CMakeLists.txt for the library, in case I messed up something:

 cmake_minimum_required(VERSION 2.6) project(foo) add_definitions(-std=c++0x) set(CMAKE_BUILD_TYPE Debug) set(sources Foo.cpp ) add_library(foo SHARED ${sources}) 
+4
source share
1 answer

You need to export types from a shared library. This is done using the __declspec(dllexport) and __declspec(dllimport) . Read the MSDN documentation; it is rather connected.

Since the header must have __declspec(dllexport) when creating the library and __declspec(dllimport) when compiling the code that uses it, a character is usually defined, usually called LIBRARYNAME_EXPORT and #ifdefs, depending on whether LIBRARYNAME_EXPORTS defined.

CMake automatically detects target_EXPORTS when creating a (shared) library. It can be overridden by setting the DEFINE_SYMBOL target property.

Unix chooses a different path and by default exports and imports all characters from shared libraries (except for static and explicitly hidden). This requires a small amount of performance penalties, since you need to allow more characters, but much easier to use (no changes are required to switch from static to the shared library) and much more flexible (i.e. you can redefine characters from shared libraries that you cannot do on Windows).

+2
source

Source: https://habr.com/ru/post/1441261/


All Articles