Sign in to follow this  
Uphoreum

std::string destruction == heap corruption?

Recommended Posts

I have a method in a DLL that I'm calling from an EXE which takes a plain old std::string. Even if the method does absolutely nothing, I get a "possible heap corruption" error when that passed in string gets destroyed when the method is finished. Could this issue be caused by my (unintentional) misuse of the heap somewhere else? It's a large DLL so I don't want to start looking if there is some other possible cause. All other usage of the DLL seems to work fine, so I don't know what I should look in to. Any ideas on why a string being destroyed would corrupt the heap? I wish I could give more info but I have no idea what's going on.

Share this post


Link to post
Share on other sites
Long story short: using templates across DLL boundaries is a bad idea. std::string is a typedef for std::basic_string<char, std::char_traits<char>, std::allocator<char> >, which means it's a template. If you do use try to use a std::string across boundaries, you'll need to make sure that everything is compiled with the exact same compiler options, that exact same compiler and that all the modules share the same standard library implementations both in terms of source and in terms of instances. You can also DLL export a template instance ( example), but it does not work with all template classes. On the whole, DLLs were designed for C interfaces, and using them with C++ is really a horrible hack.

Share this post


Link to post
Share on other sites
Oh dear, I'm also passing std::vectors around :-/

Okay, I might have to find an alternative to using a DLL for this then. I would assume static libs don't have this issue? Static libs are pretty safe, correct?

Thanks!

EDIT: On second thought, is there any other problems besides templates and name mangling with C++ DLLs that I should know of? And, is it safer if I wrap the templates in another concrete class is that safe(er)?

Share this post


Link to post
Share on other sites
Quote:
Original post by Uphoreum
EDIT: On second thought, is there any other problems besides templates and name mangling with C++ DLLs that I should know of? And, is it safer if I wrap the templates in another concrete class is that safe(er)?
I assume you are also aware of the problems with memory allocation/deallocation across DLL boundaries?

Wrapping the std::containers in a concrete class doesn't help either - the size and layout of the std::containers may vary between releases. You can however wrap them in a class and hide them entirely using the PIMPL idiom.

Share this post


Link to post
Share on other sites
Using C++ in DLLs is fine and common, the problem is using C++ in the DLL interface. That is, it's fine to use whatever C++ you want in your DLL code, just as long as the users of your DLL don't see it.

Share this post


Link to post
Share on other sites
Quote:
Original post by Uphoreum
Okay, I might have to find an alternative to using a DLL for this then. I would assume static libs don't have this issue? Static libs are pretty safe, correct?


Hmm, perhaps not. I was creating a static library, and used an std::string (therefore, templates) as part of some of the method calls. I got a similar error message - 'possible heap corruption', which could well be due to the same problem.

I was going to post about this myself, but I found that abandoning the static library and inlining all the code got rid of the problem anyway. Still, the eventual aim was to wrap all of the code into a DLL so I can see myself running in the same problem. :(

Just to clarify, is this just a problem with templated types? So, as long as we just pass basic types - int, float, even a char* - we won't run into this problem?

Share this post


Link to post
Share on other sites
Well, what I'm getting from this is that C++ is just not particularly good at being a DLL interface, or binary compatible with anything in general :D

I'm also having problems with exceptions across DLLs. I'm going to give a static lib a try and see how that works out. Otherwise I'll just have to figure out good way to inline the code like you did.

Share this post


Link to post
Share on other sites
Yeah, using exceptions across DLL boundaries is also a bad idea. You can pass around pointers to abstract base classes as interfaces safely, but that's really about it in terms of safe C++ constructs with DLLs. Otherwise, you should generally keep your interface to constructs you can express in C.

It's possible to use C++ features across DLL boundaries, but it's fragile and not worth the effort.

Share this post


Link to post
Share on other sites
Quote:
Original post by SiCrane
Yeah, using exceptions across DLL boundaries is also a bad idea. You can pass around pointers to abstract base classes as interfaces safely, but that's really about it in terms of safe C++ constructs with DLLs. Otherwise, you should generally keep your interface to constructs you can express in C.

Hence COM, although that can be a pain in it's own way. DLLs also introduce you to the wonderful world of DLL Hell, enjoy your version management.

Edit: my ability to spell is inversly proportional to the speed of this site on the iPhone (aka horrible)

[Edited by - Washu on August 14, 2009 9:16:19 PM]

Share this post


Link to post
Share on other sites
That's the old well known problem in Ogre3D. That is why I hate that engine, and I hate its bloated mechanisms of plugins and DLLs. Would be better have them in a single monolithic static library.

You should pass constant string references ( const char * ) rather than string objects.
Also you must be aware of memory allocation. STL allows to define custom allocators, so you should use the same allocator in every DLL.

Share this post


Link to post
Share on other sites
In case anyone is interested, I found an awesome page about CPP DLL ABI compatibility:

It turns out I was actually doing a lot of this stuff to hide implementation. I didn't know it had any effect on ABI compatibility.

Share this post


Link to post
Share on other sites
Sorry to revive this somewhat old thread, but I have a quick clarification question. So, if I have, for example, mylib.h which contains struct and function declarations wrapped in extern "C", and then I have mylib.cpp which defines those functions internally using C++ features, will I be able to maintain all the ABI compatibility that C provides? Will the resulting DLL be usable by any C/C++ compiler even though it internally uses C++?

Share this post


Link to post
Share on other sites
Well, it can be compiled with a C compiler just fine. No overloading, no C++ specific features (templates, etc.). Just for clarification, what I'm doing here is creating a DLL with a C interface to be called from other languages like Java with JNA, C# with P/Invoke, and Python with ctypes. Right now it is entirely C, but I'm thinking of implementing some things that would make good use of vector and hash_map/map and I don't really want to implement those things myself so much.

Share this post


Link to post
Share on other sites
That should be safe enough, provided that all the compilers involved agree on struct member padding.

Share this post


Link to post
Share on other sites
Regarding the problems with separate heaps for DLLs, you can avoid this by overloading new in the DLL's code with a function that calls ::new() from the main binary.

Of course you will need a way to transfer the main binaries version of new to the DLL. The obvious way is to use a global function pointer in the DLL that gets pointed to the main binaries' ::new() as soon as possible after loading the DLL. (You do need to be careful that the DLL doesn't call new before this pointer is initialized).

You also have to avoid using other forms of heap use such as malloc, or third-party libraries that may use these from either side of the DLL boundary.

Lastly, you need to ensure that all object code for the DLL #includes the overload of ::new().

I haven't had any issues with this. I have only tried a few test cases, but I can't foresee any problems.



Although my code is quite a way away from doing this, I intend to enforce a compiler version check when loading my DLLs so that I can use C++ across DLL boundaries with no problems. This could be annoying for developers of the DLLs (especially as the main binaries' version may change), but I think the cost is less than that of avoiding passing classes across the boundary.

I'm not sure how I'm going to deal with exceptions. Perhaps I'll have to require that all cross-DLL calls are declared as throw() (or noexcept if that gets accepted).


ASIDE: Wouldn't it be nice if compilers would include a layout version variable that was only modified when variable layout could change (or when standard library members had their members altered). That way you could ensure object compatibility across the DLL boundary for many different compiler versions.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this