How to expose STL list over DLL boundary?
Asked Answered
A

3

8

I have a DLL which needs to access data stored in STL containers in the host application. Because C++ has no standard ABI, and I want to support different compilers, the interface between the application and DLL basically has to remain plain-old-data.

For vectors this is relatively straightforward. You can simply return the memory block of the vector, because it is guaranteed to be contigious:

// To return vector<int> data
virtual void GetVectorData(const int*& ptr, size_t& count) const
{
    if (!vec.empty())
        ptr = &(vec.front());

    count = vec.size();
}

Now the DLL can have safe read-only access to the vector's data via that interface. The DLL can also wrap this to copy the contents in to a vector for itself as well.

What about STL lists (and deques) though? Is there another straightforward way to allow access via a DLL boundary? Or will I have to resort to some kind of GetFirst()/GetNext() interface? I might need to do this for a lot of lists, so it'd be nice to have a solution as simple as vector's.

Aurelioaurelius answered 10/12, 2009 at 14:56 Comment(0)
C
7

Perhaps you can pass something like "handles" to list/deque iterators? These handle types would be opaque and declared in a header file you would ship to the users. Internally, you would need to map the handle values to list/deque iterators. Basically, the user would write code like:

ListHandle lhi = GetListDataBegin();
const ListHandle lhe = GetListDataEnd();

while (lhi != lhe)
{
  int value = GetListItem(lhi);
  ...
  lhi = GetNextListItem(lhi);
}
Catina answered 10/12, 2009 at 15:23 Comment(0)
R
10

You can pass stl objects between DLLs and support different compilers if you are careful where you instantiate each stl type. You need some intelligent "DLLEXPORT" macros -- I use the following set to successfully support VC and gcc.

#ifdef WIN32
#ifdef MYDLLLIB_EXPORTS      // DLL export macros
#define MYDLLLIB_API __declspec(dllexport)
#define MYDLLLIB_TEMPLATE
#else
#define MYDLLLIB_API __declspec(dllimport)
#define MYDLLLIB_TEMPLATE extern
#endif
#else                       // Not windows --- probably *nix/bsd
#define MYDLLLIB_API
#ifdef MYDLLLIB_EXPORTS
#define MYDLLLIB_TEMPLATE
#else
#define MYDLLLIB_TEMPLATE extern
#endif
#endif // WIN32

When compiling your DLL, define MYDLLLIB_EXPORTS. In the DLL you can then instantiate each stl type you wish to use, for example, lists or vectors of strings

MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::vector<std::string>;
MYDLLLIB_TEMPLATE template class MYDLLLIB_API std::list<std::string>;

Consumers of your DLL (who don't have MYDLLLIB_EXPORTS defined) will then see

extern template class __declspec(dllimport) std::vector<std::string>;

and use the binary code exported from your DLL instead of instantiating their own.

Redeem answered 11/12, 2009 at 9:49 Comment(4)
That's a very interesting solution - would I have to do the same for std::vector<T>::iterator and std::list<T>::iterator? Is it the same way round for the DLL consuming STL classes from a host EXE?Aurelioaurelius
You don't have to instantiate the iterators because when you instantiate a specific stl container class you also get code for all classes within the container class. Don't know about consuming STL classes from a host EXE -- I haven't personally tried that so if you wish to follow this method you will have to do a bit of experimentation with a simple test.Redeem
Here it is in MSDN: support.microsoft.com/default.aspx?scid=kb;EN-US;Q168958Towny
Doesn't the MSDN article (question 168958) state that list, map and other containers can NOT be exported, only vector can?Chalcocite
C
7

Perhaps you can pass something like "handles" to list/deque iterators? These handle types would be opaque and declared in a header file you would ship to the users. Internally, you would need to map the handle values to list/deque iterators. Basically, the user would write code like:

ListHandle lhi = GetListDataBegin();
const ListHandle lhe = GetListDataEnd();

while (lhi != lhe)
{
  int value = GetListItem(lhi);
  ...
  lhi = GetNextListItem(lhi);
}
Catina answered 10/12, 2009 at 15:23 Comment(0)
N
1

the interface between the application and DLL basically has to remain plain-old-data.

Not necessarily. You have to be sure that the same compiler version is used. Also, build settings that impact the layout of the STL objects is exactly the same between the dll and application.

If you were to release the dll out into the wild, you are right to be concerned with exposing STL across dll boundaries. If, however, everything is under your control and purely internal (or if you can rigidly enforce 3rd party build settings/compiler) you should be fine.

Nuristan answered 10/12, 2009 at 15:0 Comment(4)
You're right that I could get away with it if all the compile settings are the same. But this is for a plugin architecture and I'd like to support different compilers. I've edited the question to clarify this.Aurelioaurelius
I agree with this, but perhaps you should stress that it's not just the layout - the code must have been compiled with the same version of the compiler, so that implementations of methods like new and delete match up.Esquire
If you're rigidly enforcing build settings / compiler versions and everything is under your control, what, then, is the advantage of DLLs versus using static libraries and linking everything together into one executable?Reremouse
@MartinB One advantage is that you can re-use the same shared module across multiple internal applications. If only one application is being developed, then there's no advantage over static linking.Timberhead

© 2022 - 2024 — McMap. All rights reserved.