Let’s say you have the following function:
-
void AppendChar(std::string& s, char ch)
-
{
-
s += ch;
-
}
What happens if this function is exported as an ordinal function from a DLL (not an inlined piece of code inside a header) and you call it from an EXE?
It works most of the time. When it doesn’t, it corrupts your heap and causes a spectacular mess.
In Windows you must free memory with the same allocator that allocated it. However, your EXE may not share the same allocator as the DLL. Perhaps the two modules are linked against different versions of libc, or perhaps one of the modules is using a static version of libc. If your EXE and DLL do not share an allocator and if AppendChar resizes the string s, you will almost certainly cause a heap corruption.
The STL performs a lot of reallocations behind the scenes for you; this is one of its major benefits. Unfortunately, if you are writing a general-purpose DLL these behind-the-scene allocations are deadly. You cannot know or dictate what version of libc your clients will use.
Therefore, I reiterate my previous recommendation:
Avoid passing STL objects as parameters to DLLs.
January 4th, 2008 at 11:54 pm
I think it was _C++ Coding Standards_ that, for this reason, recommends using vector of char to pass strings across API/calling convention bounds, and avoiding all other containers and objects. Similarly, I think they recommended the use of boost/tr1::shared_ptr in vectors for passing anything but native types (for which we do have some guarantees), as the shared pointer can carry the deallocator along with it. But, then, they also recommend avoiding passing anything particularly fancy in general.
But I could be rusty on that.
On Windows, we find it’s easy not to fight The Windows Way. Native types, BSTRs, VARIANTs, and so forth are just much easier to not get into sticky situations. We prefer the containers Microsoft provides on Windows wen we’re working with stuff that’s going to cross the API boundary, because it’s a lot less hassle, and then stick to the STL for the inner guts, when appropriate. Some things, like strings, are just a lot easer to keep in CString and such, though, and not try to fight the convenience, especially when dealing with internationalization. YMMV.
January 5th, 2008 at 5:53 am
“one of the modules is using a static version of libc”: That in itself is a recipe for disaster for a module. However being linked against a different version of libc is not a problem in that case as long as they are ABI compatible (which must be if you want your code to run at all). All modules will use the same allocators even if it is not necessarily the one they were built against.
Also, using the matching deallocator in not a Windows specificity. This is the general rule no matter the platform.
January 7th, 2008 at 9:48 am
Sam,
It also happens to be the default setting for Visual Studio 6.0 and perhaps later versions as well.
This isn’t true on Windows; different libcs do not share allocators. Consider if the EXE is build against libc A and the DLL is build against libc B. The EXE will allocate a string using libc A. The DLL will then free the string’s allocation using libc B and reallocate the string using libc B. Finally, the EXE will deallocate the string using libc A.
The only case where this works is if the object carries around its own allocator or if both the EXE and DLL are linked against the same DLL-based version of libc and thus share an allocator.
November 15th, 2008 at 1:03 pm
> Steve:
> The only case where this works is if the object carries around its own allocator
Because all stl-containers carry around their own allocator it is possible to pass stl containers across module boundaries. And here is how it works:
I’ve written an allocator that stores the translation unit’s default raw allocation/deallocation operators (::operator new/::operator delete).
I called this allocator modulebound_allocator. Because it’s a stateful allocator there are certain things to consider when copying/moving the container, though, but it works great for strings in a larger software project.