I've read several times that passing STL objects like vector and string outside of a DLL boundary is bad practice because different compiler versions can generate different code for STL objects. Therefore, you should design a C-style interface and not pass STL objects at all. However, there are still some things unclear to me:
1. What is the 'boundary' of a DLL?
Is it right to say, that the boundary is where code is beeing compiled on DLL side? What if I define a .h file inside a DLL (f.e. to write a factory class) and use that header file in a different project? Is that .h file inside or outside the boundary of the DLL and why?
2. What is contained in a DLL?
Let' say I have a class Foo:
class Foo
{
public:
__declspec(dllexport) void f1(); //instantiates v1 inside function
private:
unique_ptr<vector<int>> v1 = nullptr;
}
If I only mark the function f1() with __declspec(dllexport), only this function should be contained in the DLL. How does the code inside f1() know what v1 is if v1 isn't contained in the DLL?
3. Passing objects out of a DLL-boundary using unique_ptr
I'm using unique_ptr almost everytime in my project. From what I understand, returning a unique_ptr from a DLL would be bad practice because unique_ptr is an STL object. How can I instantiate an object inside the DLL and return a unique_ptr to it?
4. Why does defining interfaces or using PIMPL help to define an DLL interface?
I still have to convert my STL classes to C-style objects. And in the project using the DLL, I would have to somehow wrap the C-style objects inside STL classes again. I don't see any advantage of using interfaces or PIMPL in this case.
Also, if I define an interface (class with pure virtual functions), wouldn't this have the same effect as just declaring the functions in my class with __declspec(dllexport)?
class IFoo
{
public:
virtual ~IFoo() = 0 {};
virtual void f1() = 0;
}
class Foo : public IFoo
{
public:
void f1();
//__declspec(dllexport) void f1(); //why use an interface if I can just declare the functions like this?
}
How is the DLL-STL problematic solved in modern C++ 11/14 libraries? Are there any modern open-source libraries that I can have a look at?
Unfortunately STL types aren't consistent across compilers. Even different versions of Visual Studio have differences.
The boundary is where the code is compiled. If you have an implementation in a header file in your library, then the compiler used to compile the EXE will compile the code. This is potentially very bad because what the code in the EXE thinks is the data is different to what the code in the DLL thinks is the data. (You need to look out for differences like this especially if you have #ifs in a struct definition and you need to be explicit about packing).
The only way to be sure is to define all your own types (being careful of packing) and not use STL. This is what DLL libraries usually do.
Interfaces can enable the user to dynamically link to the library. Using __declspec(dllexport) requires a static linking; that is the EXE has to link to the .lib generated when you compiled the DLL to be able to access all the functions. This means amongst other things you can't update the DLL without the EXE having to be recompiled (probably - you can get away with this in some circumstances, but it's not a good idea).
By dynamically linking you can update the DLL or add functionality to the DLL without relinking the EXE as long as you don't change your interfaces. The EXE might call LoadLibrary() on the DLL and GetProcAddress() to access one function that returns an interface. Everything else including data types passed as parameters are interfaces (i.e. contain only pure virtual functions) or simple structs. This is how the basic level of COM works.
To answer question 2, when you declare something as __declspec(dllexport) you are stating that this is part of the interface to the DLL - something that is accessible to the component that loads the DLL. Anything declared without __declspec(dllexport) should be present within the DLL but will not be available to be called/used by an external component.
Related
I'm trying to export classes from a DLL that contain objects such as std::vectors and std::strings - the whole class is declared as DLL export through:
class DLL_EXPORT FontManager {
The problem is that for members of the complex types I get this warning:
warning C4251: 'FontManager::m__fonts' : class 'std::map<_Kty,_Ty>' needs to have dll-interface to be used by clients of class 'FontManager'
with
[
_Kty=std::string,
_Ty=tFontInfoRef
]
I'm able to remove some of the warnings by putting the following forward class declaration before them even though I'm not changing the type of the member variables themselves:
template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;
template class DLL_EXPORT std::vector<tCharGlyphProviderRef,std::allocator<tCharGlyphProviderRef> >;
std::vector<tCharGlyphProviderRef> m_glyphProviders;
Looks like the forward declaration "injects" the DLL_EXPORT for when the member is compiled but is it safe?
Does it really change anything when the client compiles this header and uses the std:: container on his side?
Will it make all future uses of such a container DLL_EXPORT (and possibly not inline)?
And does it really solve the problem that the warning tries to warn about?
Is this warning anything I should be worried about or would it be best to disable it in the scope of these constructs?
The clients and the DLL will always be built using the same set of libraries and compilers and those are header only classes...
I'm using Visual Studio 2003 with the standard STD library.
Update
I'd like to target you more though as I see the answers are general and here we're talking about std containers and types (such as std::string) - maybe the question really is:
Can we disable the warning for standard containers and types available to both the client and the DLL through the same library headers and treat them just as we'd treat an int or any other built-in type? (It does seem to work correctly on my side)
If so would should be the conditions under which we can do this?
Or should maybe using such containers be prohibited or at least ultra care taken to make sure no assignment operators, copy constructors etc will get inlined into the DLL client?
In general I'd like to know if you feel designing a DLL interface having such objects (and for example using them to return stuff to the client as return value types) is a good idea or not and why, I'd like to have a "high level" interface to this functionality...
Maybe the best solution is what Neil Butterworth suggested - creating a static library?
When you touch a member in your class from the client, you need to provide a DLL-interface.
A DLL-interface means, that the compiler creates the function in the DLL itself and makes it importable.
Because the compiler doesn't know which methods are used by the clients of a DLL_EXPORTED class it must enforce that all methods are dll-exported.
It must enforce that all members which can be accessed by clients must dll-export their functions too. This happens when the compiler is warning you of methods not exported and the linker of the client sending errors.
Not every member must be marked with with dll-export, e.g. private members not touchable by clients. Here you can ignore/disable the warnings (beware of compiler generated dtor/ctors).
Otherwise the members must export their methods.
Forward declaring them with DLL_EXPORT does not export the methods of these classes. You have to mark the according classes in their compilation-unit as DLL_EXPORT.
What it boils down to ... (for not dll-exportable members)
If you have members which aren't/can't be used by clients, switch off the warning.
If you have members which must be used by clients, create a dll-export wrapper or create indirection methods.
To cut down the count of externally visible members, use approaches such as the PIMPL idiom.
template class DLL_EXPORT std::allocator<tCharGlyphProviderRef>;
This does create an instantiation of the template specialization in the current compilation unit. So this creates the methods of std::allocator in the dll and exports the corresponding methods. This does not work for concrete classes as this is only an instantiation of template classes.
That warning is telling you that users of your DLL will not have access to your container member variables across the DLL boundary. Explicitly exporting them makes them available, but is it a good idea?
In general, I'd avoid exporting std containers from your DLL. If you can absolutely guarantee your DLL will be used with the same runtime and compiler version you'd be safe. You must ensure memory allocated in your DLL is deallocated using the same memory manager. To do otherwise will, at best, assert at runtime.
So, don't expose containers directly across DLL boundaries. If you need to expose container elements, do so via accessor methods. In the case you provided, separate the interface from the implementation and expose the inteface at the DLL level. Your use of std containers is an implementation detail that the client of your DLL shouldn't need to access.
Alternatively, do what Neil suggest and create a static library instead of a DLL. You lose the ability to load the library at runtime, and consumers of your library must relink anytime you change your library. If these are compromises you can live with, a static library would at least get you past this problem. I'll still argue you're exposing implementation details unnecessarily but it might make sense for your particular library.
There are other issues.
Some STL containers are "safe" to export (such as vector), and some aren't (e.g. map).
Map for instance is unsafe because it (in the MS STL distribution anyway) contains a static member called _Nil, the value of which is compared in iteration to test for the end. Every module compiled with STL has a different value for _Nil, and so a map created in one module will not be iterable from another module (it never detects the end and blows up).
This would apply even if you statically link to a lib, since you can never guarantee what the value of _Nil will be (it's uninitialised).
I believe STLPort doesn't do this.
One alternative that few people seem to consider is not to use a DLL at all but to link statically against a static .LIB library. If you do that, all the issues of exporting/importing go away (though you will still have name-mangling issues if you use different compilers). You do of course lose the features of the DLL architecture, such as run-time loading of functions, but this can be a small price to pay in many cases.
The best way I found to handle this scenario is:
create your library, naming it with the compiler and stl versions included in the library name, exactly like boost libraries do.
examples:
- FontManager-msvc10-mt.dll for dll version, specific for MSVC10 compiler, with the default stl.
- FontManager-msvc10_stlport-mt.dll for dll version, specific for MSVC10 compiler, with the stl port.
- FontManager-msvc9-mt.dll for dll version, specific for MSVC 2008 compiler, with the default stl
- libFontManager-msvc10-mt.lib for static lib version, specific for MSVC10 compiler, with the default stl.
following this pattern, you will avoid problems related with different stl implementations. remember, the stl implementation in vc2008 differs from the stl implementation in the vc2010.
See your example using boost::config library:
#include <boost/config.hpp>
#ifdef BOOST_MSVC
# pragma warning( push )
# pragma warning( disable: 4251 )
#endif
class DLL_EXPORT FontManager
{
public:
std::map<int, std::string> int2string_map;
}
#ifdef BOOST_MSVC
# pragma warning( pop )
#endif
Found this article. In short Aaron has the 'real' answer above; Don't expose standard containers across library boundaries.
Though this thread is pretty old, I found a problem recently, which made me think again about having templates in my exported classes:
I wrote a class which had a private member of type std::map. Everything worked quite well untill it got compiled in release mode, Even when used in a build system, which ensures that all compiler settings are the same for all targets. The map was completely hidden and nothing was directly exposed to the clients.
As a result the code was just crashing in release mode. I gues, because different binary std::map instances were created for implementation and client code.
I guess the C++ Standard is not saying anaything about how this shall be handled for exported classes as this is pretty much compiler specific. So I guess the biggest portability rule is to just expose Interfaces and use the PIMPL idiom as much as possible.
Thanks for any enlightenment
In such cases, consider the uses of the pimpl idiom. Hide all the complex types behind a single void*. Compiler typically fails to notice that your members are private and all methods included in the DLL.
none of the workarounds above are acceptable with MSVC because of the static data members inside template classes like stl containers
each module (dll/exe) gets its own copy of each static definition...wow! this will lead to terrible things if you somehow 'export' such data (as 'pointed' above)...so don't try this at home
see http://support.microsoft.com/kb/172396/en-us
Exporting classes containing std:: objects (vector, map, etc) from a dll
Also see Microsoft's KB 168958 article How to export an instantiation of a Standard Template Library (STL) class and a class that contains a data member that is an STL object. From the article:
To Export an STL Class
In both the DLL and the .exe file, link with the same DLL version of the C run time. Either link both with Msvcrt.lib (release build) or
link both with Msvcrtd.lib (debug build).
In the DLL, provide the __declspec specifier in the template instantiation declaration to export the STL class instantiation from
the DLL.
In the .exe file, provide the extern and __declspec specifiers in the template instantiation declaration to import the class from the
DLL. This results in a warning C4231 "nonstandard extension used :
'extern' before template explicit instantiation." You can ignore this
warning.
And:
To Export a Class Containing a Data Member that Is an STL Object
In both the DLL and the .exe file, link with the same DLL version of the C run time. Either link both with Msvcrt.lib (release build) or
link both with Msvcrtd.lib (debug build).
In the DLL, provide the __declspec specifier in the template instantiation declaration to export the STL class instantiation from
the DLL. NOTE: You cannot skip step 2. You must export the
instantiation of the STL class that you use to create the data member.
In the DLL, provide the __declspec specifier in the declaration of the class to export the class from the DLL.
In the .exe file, provide the __declspec specifier in the declaration of the class to import the class from the DLL. If the
class you are exporting has one or more base classes, then you must
export the base classes as well. If the class you are exporting
contains data members that are of class type, then you must export the
classes of the data members as well.
If you use a DLL make initialization of all objects at event "DLL PROCESS ATTACH" and export a pointer to its classes/objects.
You may provide specific functions to create and destroy objects and functions to obtain the pointer of the objects created, so you can encapsulate these calls in a wrapper class of access at include file.
The best approach to use in such scenarios is to use the PIMPL design pattern.
When using dynamic libraries, I understand that we should only pass Plain Old Data-structures across boundaries. So can we pass a pointer to base ?
My idea is that the application and the library could both be aware of a common Interface (pure virtual method, = 0).
The library could instantiate a subtype of that Interface,
And the application could use it.
For instance, is the following snippet safe ?
// file interface.h
class IPrinter{
virtual void print(std::string str) = 0;
};
-
// file main.cpp
int main(){
//load plugin...
IPrinter* printer = plugin_get_printer();
printer->print( std::string{"hello"} );
}
-
// file plugin.cpp (compiled by another compiler)
IPrinter* plugin_get_printer(){
return new PrinterImpl{};
}
This snippet is not safe:
the two sides of your DLL boundaries do not use the same compiler. This means that the name mangling (for function names) and the vtable layout (for virtual functions) might not be the same (implementation specific.
the heap on both sides may also be managed differently, thus you have risks related to the deleting of your object if it's not done in the DLL.
This article presents very well the main challenges with binary compatible interfaces.
You may however pass to the other side of the mirror a pointer, as part of a POD as long as the other part doesn't us it by iself (f.ex: your app passes a pointer to a configuration object to the DLL. Later another DLL funct returns that pointer to your app. Your app can then use it as expected (at least if it wasn't a pointer to a local object that no longer exists) .
The presence of virtual functions in your class means that your class is going to have a vtable, and different compilers implement vtables differently.
So, if you use classes with virtual methods across DLL calls where the compiler used on the other side is different from the compiler that you are using, the result is likely to be spectacular crashes.
In your case, the PrinterImpl created by the DLL will have a vtable constructed in a certain way, but the printer->print() call in your main() will attempt to interpret the vtable of IPrinter in a different way in order to resolve the print() method call.
I followed the guide at CodeProject and built a DLL with an abstract interface and exported the factory functions using the extern "C" command along with __declspec(dllexport) and __cdecl and by doing this the article claims that the DLL becomes compiler independent with a clean interface. However when using the DLL on two different versions of g++ the non-standard ABI created the standard c++ problems with DLL calling. In addition to the CodeProject article I also used an article from MinGW in order to be able to create multiple instances of the class.
How is it then possible to make the DLL compiler independent? and if this is not possible, is it then possible to instantiate a class within a DLL and make the functions pure C but calling the C++ functions related to the instantiated class?
Have you tried to reproduce exactly the example that was given there? And what was the failure? You have to consider that not only your class, but any other class like std::string can't be used directly. That said, I wouldn't try to reinvent the wheel but use some kind of component framework like COM.
To answer your second part, consider this:
// internal class
struct X
{
void jump();
};
// compiler-independent C interface
struct X;
struct X* x_alloc(void);
void x_jump(struct X* x, float height);
void x_free(struct X* x);
This works. I have no idea what exactly you tried and what didn't work for you, there's just not enough info to tell.
I have an unmanaged Win32 C++ application that uses multiple C++ DLLs. The DLLs each need to use class Foo - definition and implementation.
Where do Foo.h and Foo.cpp live so that the DLLs link and don't end up duplicating code in memory?
Is this a reasonable thing to do?
[Edit]
There is a lot of good info in all the answers and comments below - not just the one I've marked as the answer. Thanks for everyone's input.
Providing functionality in the form of classes via a DLL is itself fine. You need to be careful that you seperate the interrface from the implementation, however. How careful depends on how your DLL will be used. For toy projects or utilities that remain internal, you may not need to even think about it. For DLLs that will be used by multiple clients under who-knows-which compiler, you need to be very careful.
Consider:
class MyGizmo
{
public:
std::string get_name() const;
private:
std::string name_;
};
If MyGizmo is going to be used by 3rd parties, this class will cause you no end of headaches. Obviously, the private member variables are a problem, but the return type for get_name() is just as much of a problem. The reason is because std::string's implementation details are part of it's definition. The Standard dictates a minimum functionality set for std::string, but compiler writers are free to implement that however they choose. One might have a function named realloc() to handle the internal reallocation, while another may have a function named buy_node() or something. Same is true with data members. One implementation may use 3 size_t's and a char*, while another might use std::vector. The point is your compiler might think std::string is n bytes and has such-and-so members, while another compiler (or even another patch level of the same compiler) might think it looks totally different.
One solution to this is to use interfaces. In your DLL's public header, you declare an abstract class representing the useful facilities your DLL provides, and a means to create the class, such as:
DLL.H :
class MyGizmo
{
public:
static MyGizmo* Create();
virtual void get_name(char* buffer_alloc_by_caller, size_t size_of_buffer) const = 0;
virtual ~MyGizmo();
private:
MyGizmo(); // nobody can create this except myself
};
...and then in your DLL's internals, you define a class that actually implements MyGizmo:
mygizmo.cpp :
class MyConcreteGizmo : public MyGizmo
{
public:
void get_name(char* buf, size_t sz) const { /*...*/ }
~MyGizmo() { /*...*/ }
private:
std::string name_;
};
MyGizmo* MyGizmo::Create()
{
return new MyConcreteGizmo;
}
This might seem like a pain and, well, it is. If your DLL is going to be only used internally by only one compiler, there may be no reason to go to the trouble. But if your DLL is going to be used my multiple compilers internally, or by external clients, doing this saves major headaches down the road.
Use __declspec dllexport to export the class to the DLL's export table, then include the header file in your other projects and link against the main DLL's export library file. That way the implementation is common.
Where does Foo live? in another dll.
Is it reasonable? not really.
If you declare a class like this:
class __declspec(dllexport) Foo { ...
then msvc will export every member function of the class. However the resulting dll is very fragile as any small change to the class definition without a corresponding rebuild of every consuming dll means that the consuming code will allocate the incorrect number of bytes for any stack and heap allocations not performed by factory functions. Likewise, inline methods will compile into consuming dlls and reference the old layout of the class.
If all the dlls are always rebuilt together, then go ahead. If not - don't :P
This question is related to "How to make consistent dll binaries across VS versions ?"
We have applications and DLLs built
with VC6 and a new application built
with VC9. The VC9-app has to use
DLLs compiled with VC6, most of
which are written in C and one in
C++.
The C++ lib is problematic due to
name decoration/mangling issues.
Compiling everything with VC9 is
currently not an option as there
appear to be some side effects.
Resolving these would be quite time
consuming.
I can modify the C++ library, however it must be compiled with VC6.
The C++ lib is essentially an OO-wrapper for another C library. The VC9-app uses some static functions as well as some non-static.
While the static functions can be handled with something like
// Header file
class DLL_API Foo
{
int init();
}
extern "C"
{
int DLL_API Foo_init();
}
// Implementation file
int Foo_init()
{
return Foo::init();
}
it's not that easy with the non-static methods.
As I understand it, Chris Becke's suggestion of using a COM-like interface won't help me because the interface member names will still be decorated and thus inaccessible from a binary created with a different compiler. Am I right there?
Would the only solution be to write a C-style DLL interface using handlers to the objects or am I missing something?
In that case, I guess, I would probably have less effort with directly using the wrapped C-library.
The biggest problem to consider when using a DLL compiled with a different C++ compiler than the calling EXE is memory allocation and object lifetime.
I'm assuming that you can get past the name mangling (and calling convention), which isn't difficult if you use a compiler with compatible mangling (I think VC6 is broadly compatible with VS2008), or if you use extern "C".
Where you'll run into problems is when you allocate something using new (or malloc) from the DLL, and then you return this to the caller. The caller's delete (or free) will attempt to free the object from a different heap. This will go horribly wrong.
You can either do a COM-style IFoo::Release thing, or a MyDllFree() thing. Both of these, because they call back into the DLL, will use the correct implementation of delete (or free()), so they'll delete the correct object.
Or, you can make sure that you use LocalAlloc (for example), so that the EXE and the DLL are using the same heap.
Interface member names will not be decorated -- they're just offsets in a vtable. You can define an interface (using a C struct, rather than a COM "interface") in a header file, thusly:
struct IFoo {
int Init() = 0;
};
Then, you can export a function from the DLL, with no mangling:
class CFoo : public IFoo { /* ... */ };
extern "C" IFoo * __stdcall GetFoo() { return new CFoo(); }
This will work fine, provided that you're using a compiler that generates compatible vtables. Microsoft C++ has generated the same format vtable since (at least, I think) MSVC6.1 for DOS, where the vtable is a simple list of pointers to functions (with thunking in the multiple-inheritance case). GNU C++ (if I recall correctly) generates vtables with function pointers and relative offsets. These are not compatible with each other.
Well, I think Chris Becke's suggestion is just fine. I would not use Roger's first solution, which uses an interface in name only and, as he mentions, can run into problems of incompatible compiler-handling of abstract classes and virtual methods. Roger points to the attractive COM-consistent case in his follow-on.
The pain point: You need to learn to make COM interface requests and deal properly with IUnknown, relying on at least IUnknown:AddRef and IUnknown:Release. If the implementations of interfaces can support more than one interface or if methods can also return interfaces, you may also need to become comfortable with IUnknown:QueryInterface.
Here's the key idea. All of the programs that use the implementation of the interface (but don't implement it) use a common #include "*.h" file that defines the interface as a struct (C) or a C/C++ class (VC++) or struct (non VC++ but C++). The *.h file automatically adapts appropriately depending on whether you are compiling a C Language program or a C++ language program. You don't have to know about that part simply to use the *.h file. What the *.h file does is define the Interface struct or type, lets say, IFoo, with its virtual member functions (and only functions, no direct visibility to data members in this approach).
The header file is constructed to honor the COM binary standard in a way that works for C and that works for C++ regardless of the C++ compiler that is used. (The Java JNI folk figured this one out.) This means that it works between separately-compiled modules of any origin so long as a struct consisting entirely of function-entry pointers (a vtable) is mapped to memory the same by all of them (so they have to be all x86 32-bit, or all x64, for example).
In the DLL that implements the the COM interface via a wrapper class of some sort, you only need a factory entry point. Something like an
extern "C" HRESULT MkIFooImplementation(void **ppv);
which returns an HRESULT (you'll need to learn about those too) and will also return a *pv in a location you provide for receiving the IFoo interface pointer. (I am skimming and there are more careful details that you'll need here. Don't trust my syntax) The actual function stereotype that you use for this is also declared in the *.h file.
The point is that the factory entry, which is always an undecorated extern "C" does all of the necessary wrapper class creation and then delivers an Ifoo interface pointer to the location that you specify. This means that all memory management for creation of the class, and all memory management for finalizing it, etc., will happen in the DLL where you build the wrapper. This is the only place where you have to deal with those details.
When you get an OK result from the factory function, you have been issued an interface pointer and it has already been reserved for you (there is an implicit IFoo:Addref operation already performed on behalf of the interface pointer you were delivered).
When you are done with the interface, you release it with a call on the IFoo:Release method of the interface. It is the final release implementation (in case you made more AddRef'd copies) that will tear down the class and its interface support in the factory DLL. This is what gets you correct reliance on a consistent dynamic stoorage allocation and release behind the interface, whether or not the DLL containing the factory function uses the same libraries as the calling code.
You should probably implement IUnknown:QueryInterface (as method IFoo:QueryInterface) too, even if it always fails. If you want to be more sophisticated with using the COM binary interface model as you have more experience, you can learn to provide full QueryInterface implementations.
This is probably too much information, but I wanted to point out that a lot of the problems you are facing about heterogeneous implementations of DLLs are resolved in the definition of the COM binary interface and even if you don't need all of it, the fact that it provides worked solutions is valuable. In my experience, once you get the hang of this, you will never forget how powerful this can be in C++ and C++ interop situations.
I haven't sketched the resources you might need to consult for examples and what you have to learn in order to make *.h files and to actually implement factory-function wrappers of the libraries you want to share. If you want to dig deeper, holler.
There are other things you need to consider too, such as which run-times are being used by the various libraries. If no objects are being shared that's fine, but that seems quite unlikely at first glance.
Chris Becker's suggestions are pretty accurate - using an actual COM interface may help you get the binary compatibility you need. Your mileage may vary :)
not fun, man. you are in for a lot of frustration, you should probably give this:
Would the only solution be to write a
C-style DLL interface using handlers
to the objects or am I missing
something? In that case, I guess, I
would probably have less effort with
directly using the wrapped C-library.
a really close look. good luck.