17
votes

I have two dll-exported classes A and B. A's declaration contains a function which uses a std::vector in its signature like:

class EXPORT A{
 // ...
 std::vector<B> myFunction(std::vector<B> const &input);
};

(EXPORT is the usual macro to put in place _declspec(dllexport)/_declspec(dllimport) accordingly.)

Reading about the issues related to using STL classes in a DLL interface, I gather in summary:

  • Using std::vector in a DLL interface would require all the clients of that DLL to be compiled with the same version of the same compiler because STL containers are not binary compatible. Even worse, depending on the use of that DLL by clients conjointly with other DLLs, the ''instable'' DLL API can break these client applications when system updates are installed (e.g. Microsoft KB packages) (really?).

  • Despite the above, if required, std::vector can be used in a DLL API by exporting std::vector<B> like:

    template class EXPORT std::allocator<B>;
    template class EXPORT std::vector<B>;
    

    though, this is usually mentioned in the context when one wants to use std::vector as a member of A (http://support.microsoft.com/kb/168958).

  • The following Microsoft Support Article discusses how to access std::vector objects created in a DLL through a pointer or reference from within the executable (http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q172396). The above solution to use template class EXPORT ... seems to be applicable too. However, the drawback summarized under the first bullet point seems to remain.

  • To completely get rid of the problem, one would need to wrap std::vector and change the signature of myFunction, PIMPL etc..

My questions are:

  • Is the above summary correct, or do I miss here something essential?

  • Why does compilation of my class 'A' not generate warning C4251 (class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of...)? I have no compiler warnings turned off and I don't get any warning on using std::vector in myFunction in exported class A (with VS2005).

  • What needs to be done to correctly export myFunction in A? Is it viable to just export std::vector<B> and B's allocator?

  • What are the implications of returning std::vector by-value? Assuming a client executable which has been compiled with a different compiler(-version). Does trouble persist when returning by-value where the vector is copied? I guess yes. Similarly for passing std::vector as a constant reference: could access to std::vector<B> (which might was constructed by an executable compiled with a different compiler(-version)) lead to trouble within myFunction? I guess yes again..

  • Is the last bullet point listed above really the only clean solution?

Many thanks in advance for your feedback.

2
The following thread covers some/related questions, but not all of them threadjrg
template class EXPORT std::vector<B>; works for getting all the member functions exported/imported properly. What about template functions that are not member functions? E.g. operator==()? Starting with VS 2012, I now get link errors when building the DLL because the vector comparison function is undefined.Kevin Hopps
Recommended related reading: stackoverflow.com/q/5347355/103167Ben Voigt

2 Answers

2
votes

Unfortunately, your list is very much spot-on. The root cause of this is that DLL-to-DLL or DLL-to-EXE is defined on the level of the operating system, while the the interface between functions is defined on the level of a compiler. In a way, your task is similar (although somewhat easier) to that of client-server interaction, when the client and the server lack binary compatibility.

The compiler maps what it can to the way the DLL importing and exporting is done in a particular operating system. Since language specifications give compilers a lot of liberty when it comes to binary layout of user-defined types and sometimes even built-in types (recall that the exact size of int is compiler-dependent, as long as minimal sizing requirements are met), importing and exporting from DLLs needs to be done manually to achieve binary-level compatibility.

When you use the same version of the same compiler, this last issue above does not create a problem. However, as soon as a different compiler enters the picture, all bets are off: you need to go back to the plainly-typed interfaces, and introduce wrappers to maintain nice-looking interfaces inside your code.

0
votes

I've been having the same problem and discovered a neat solution to it.
Instead of passing std:vector, you can pass a QVector from the Qt library.
The problems you quote are then handled inside the Qt library and you do not need to deal with it at all.
Of course, the cost is having to use the library and accept its slightly worse performance.
In terms of the amount of coding and debugging time it saves you, this solution is well worth it.