I have to explain my team why exporting classes from DLL is not good solution, if we plan to use that DLL from different compiler. But I can't find prove for that. Is there something in standart like "compiler shouldn't supply backward compatibility, also different compilers can implement their own style of naming exporting symbols, so exported class from DLL can be used then just by same compiler"? I know it is true, but how can I prove that? Also, if you know additional arguments for me, please, help!
-
1The biggest problem is an incompatible CRT. With Visual Studio each compiler version (and even configuration like Release/Debug) has an independent heap. The problem with that allocating memory in 1 heap you can not safely free it in a second heap. A free of a memory block from a different heap typically causes heap corruption that may look random as an application may not crash right after the heap has been corrupted. With .dlls unless you isolate the memory allocations / deallocations it will not be easy to keep all allocations / deallocations in the same heap. – drescherjm Jun 30 '14 at 17:17
-
Problem with independent heap can be solved if library gives delete function, or, with classes, if exported class acessible throw function (i.e. allocation will be on DLL side) and has destructor, isn't it? – Arkady Jun 30 '14 at 17:33
-
1Have a look at [this list](https://www.google.com/search?q=site%3Astackoverflow.com+voigt+c%2B%2B+export+class+dllexport+fragile), pretty much all of the hits are problems caused by the fragile close coupling created by exporting non-pure classes. – Ben Voigt Jun 30 '14 at 17:38
3 Answers
Here are a few points that you may find interesting/useful to tell your team.
Different compilers mangle C++ names differently. This simple name mangling issue may be possible to circumvent with an explicit .def file.
Different structure alignment issues which need the correct compiler options (-mms-bitfields, ...).
A fundamental conflict of underlying exception and memory models:
A new/delete or malloc/free in a MSVC DLL will not co-operate with a Cygwin newlib new/delete or malloc/free. One cannot free space which was allocated in a function using a different new/malloc at all.
An exception raised by an MSVC DLL will not be caught by a Cygwin executable, and vice versa.
The slow GNU SJLJ exception model, (used in GCC-3.x and earlier), is compatible with the MSVC++ model, but the new DWARF2 model, (which will be used by GCC-4.x), will be incompatible.
A more complete explanation is given here, from where I shamelessly copied the above. Seeing as you use MinGW, this should be very relevant.
Also, if you haven't done so already, take a look at the discussion for this SO question.
-
1) Isn't is possible to provide .def file to let any compiler avoid naming problem? 2) structures alignment avoidable by #pragma pack, and we already have our standart, so, that is not argument against export class. 3) Managing heap generously described by drescherjm avoidable by correct deleters and reglament: everything that was allocated inside DLL have to be destroyed there. I afraid that is also not argument against export classes. – Arkady Jun 30 '14 at 17:38
-
4) raising exceptions out of DLL is also very good argument, but not exactly against export of classes. Still, your post is very useful for me, thank you very much for explanation and links. – Arkady Jun 30 '14 at 17:39
-
@Arkady: You are welcome. Nowadays, compilers are doing a far better job at being able to talk to each other than some years ago, so a lot of the points can be counteracted -- but you need to know what you're doing and you and your team (current and future) be knowledgeable and careful enough to avoid any pitfalls. – djikay Jun 30 '14 at 17:42
-
Thank you. I need arguments against export of classes, or, if finally there is no arguments and it is good idea - to understand that :-) – Arkady Jun 30 '14 at 17:49
Different compilers use different runtime libraries. std::string
, std::vector<int>
, std::shared_ptr
all have different implementations. Classes have different layout (alignment and packing are only part of layout). Assumptions about this layout get baked into both modules if there is even a single inline
function, anywhere, as well as at object creation when memory needs to be allocated.
There's a reason that C++ has the One Definition Rule, and as soon as you start mixing compilers, or even different compiler options, that rule is violated.
These are the things that can safely be shared:
- Standard-layout data-only classes, as long as you are careful to match deallocator to allocator. You can't free across library boundaries. Also packing needs to be set identically on both sides.
- Pure virtual base classes, which contain only pure virtual instance member functions. No instance data, and no static anything. No inheritance between interfaces. And only the module with the concrete subclass where the interface(s) are implemented is allowed to cast between interface pointers.
Note that neither of these require dllexport
. Just including the same header file with the same packing options is enough, because functions are found through the v-table, not through DLL imports.
Of course you'll also want C-compatible global function exported from the DLL, to bootstrap creation of the objects. For those __declspec(dllexport)
will be used, or a module definition file. Also I recommend extern "C"
for these global exports.
I strongly recommend that you read about the "Component Object Model" (COM), which goes under various other names such as DCOM, ActiveX, etc. You don't need to use all of the same mechanics, which get quite complicated, but try to understand at a minimum how the "interfaces with virtual functions only" and "reference counting with self-deallocation" along with "inheritance graph is traversed by the object itself" schemes provide the ability to mix different languages and compilers. (Side note: the existence of COM is the reason that v-table-only classes are compatible, because COM is a Windows standard for v-table layout. It's as close as Windows gets to an ABI.)

- 277,958
- 43
- 419
- 720
-
Thank you, that exactly what I usually do, after I had to create some COM interfaces, I always export finctions and pure interfaces, because it is safe and can be handled between different compilers (different gcc and VS versions at least, it's tested). I'm looking for arguments against class exporting when we need to use dofferent compilers, if class exporting is bad idea in that situation? – Arkady Jun 30 '14 at 17:53
-
1@Arkady: It depends on what you mean by "class". I listed two subcases which avoid all the trouble. But if any non-standard-layout class with data members is exported, the compilers won't all agree on the layout, and will use different offsets when trying to access the same data member. I'm sure you understand what havoc that would cause. – Ben Voigt Jun 30 '14 at 17:56
-
@Arkady: Essentially, it is safe to export C-compatible data types and global functions. Those global functions can use C++ internally. Pure virtual interfaces are (on Windows) C-compatible with a pointer to a table of function pointers, thanks to COM layout requirements. Anything else breaks. What if one `std::string `implementation uses a small-string optimization, another uses length-prefixing in the character buffer, and another stores the length inside the `std::string` object itself? But any standard library class is subject to this problem, as well as any of your own types which – Ben Voigt Jun 30 '14 at 18:02
-
-
I mean exporting class simplest way: class EXPORT_MACROS MyClass { public: std::vector
m_ints; }; for example, i.e. not C-compatible data, not pure interfaces and not global functions. You tells anything else breaks. I know it, but I can't prove it. I can't tell to my team "Great programmer told me that", even if you have more experience then me and all my team. I hope there is something from C++ Standart Rules about that, not just experience of good programmers. – Arkady Jun 30 '14 at 18:07 -
1@Arkady: That's 100% broken. You have `#include
`, and the two compilers find two different header files named `vector`. [Game over.](http://stackoverflow.com/q/4297480/103167) You mustn't use any header files found on the system path when defining class types meant to be shared. – Ben Voigt Jun 30 '14 at 18:08 -
you answer and asnwer of djikay was equal very useful. I accepted his answer because he has less points. – Arkady Jul 02 '14 at 17:46
-
1Actually, my previous comment should have said "you mustn't use any compiler-provided header files when defining class types to be shared". OS header files such as `
` are fine, as long as all compilers are using the official one (I think that cygwin gcc doesn't). – Ben Voigt Jul 02 '14 at 17:55
Um.. I thought only extended dll can loading c++ classes and extended dll is for MFC, so only visual studio can make I gues...

- 29
- 1
- 1
- 7
-
I usually export functions and interfaces instead of classes, but as I remember, I exported classes with MinGW32(gcc4.8.1) in project once. – Arkady Jun 30 '14 at 17:26