Can a std::string be passed by value across DLL boundries between DLLs built with different version of Visual Studio?
2 Answers
No, because templated code is generated separately per module.
So, when your EXE instantiates a std::string
and passes it to the DLL, the DLL will begin using a completely different implementation on it. The result is a total mess, but it often sorta almost works because implementations are very similar, or the mess is hard to detect because it's some kind of subtle heap corruption.
Even if they're both built with the same version of VS, it's very precarious / fragile, and I would not recommend it. Either use a C-style interface between modules (for example, COM), or just don't use a DLL.
More detailed explanation here: Creating c++ DLL without static methods
and here: How can I call a function of a C++ DLL that accepts a parameter of type stringstream from C#?
-
And also, even if they are built with the same compiler / version / options, the two modules (DLL or EXE) use different heaps to manage dynamic memory allocations, and therefore, any reallocation or deletion (e.g., appending new characters at the end of a passed string object) will invoke the wrong heap and cause that heap to become corrupt and the original heap to leak. I've encountered this mess before, when I didn't know any better, and trying to debug such a problem is not something I would even wish on my worst enemy. BTW, now, I like to use Python bindings as glue between C++ modules. – Mikael Persson May 12 '14 at 16:47
-
I agree with the first word of your answer, but the justification that follows is horribly misleading and/or wrong. The point is that you can well pass strings or other objects by value between different executables and DLLs, provided they all have the same idea of what this object looks like. Getting this right is a bit tricky and has a bunch of pitfalls though, but it has nothing to do with templated code. Also, the comment that executable and DLL use a different heap is misleading, it is just one details that you have to make them both use the same heap, which can be done. – Ulrich Eckhardt May 12 '14 at 18:20
-
Technically speaking, "templated" or "inline" has not much to do with it. But in practice, I have witnessed too many times that inlined code aggravates this situation. Developers treat the std library the same as OS calls: some kind of black box DLL, not realizing that it's being custom generated & baked in for every caller. Developers don't blink an eye at sharing headers between projects. But when developers share source files, they know they're entering dangerous waters and the consequences are very clear (separate implementations for each module). But yes maybe I could make this clearer. – tenfour May 13 '14 at 09:37
-
One thing is for certain: it's a big "no-no", even if there are ways to make it work, it's just too fragile to be considered good practice. – tenfour May 13 '14 at 09:37
-
To be honest, I don't understand what you want to express here, what the "it" refers to that is a big no-no. FWIW, my experience is that you can pass C++ objects across DLL boundaries. There are basically two things (though each contains several aspects) that you need to take care of, that you don't violate the ODR and that you don't mix code from incompatible compilers, but then it works. You can also see that it works from many C++ projects on the web, like e.g. Boost. – Ulrich Eckhardt May 13 '14 at 17:40
In general, you can not mix binary code built with different compilers, which includes different versions of the same compiler (and can even include the same compiler invoked with different commandline options), so the answer to what you are trying to do is a clear "No".
The reason is that different compilers might provide different implementations of std::string. For example, one implementation could have a fixed, static buffer, while another version doesn't, which already leads to different object sizes. There is a bunch of other things that can make the interfaces incompatible, like the underlying allocator, the internal representation. Some things will already fail to link, due to name mangling or different private APIs, which both protect you from doing something wrong.
Some notes:
- Even if you didn't pass the object by value but by reference, the called code could have a different idea of what this objects looks like.
- It also doesn't matter that the type is supplied by the compiler, even if you defined the class yourself and compiled two DLLs with different versions of the class definition you would have problems. Also, if you change your standard library implementation, you make your binaries incompatible, too.
- It doesn't even matter that the other code is in a DLL, it also applies to code in the same executable or DLL, although headers and automatic recompilation on change make this case very unlikely.
- Specifically for MS Windows, you also have a debug heap and a release heap, and memory allocated in one must not be returned to the other. For that reason, you often have two DLL, one with a 'd' suffix (the debug version) and one without. This is a case where the compiler settings already affect compatibility, but you can get around this using a parallel approach of providing two versions of your DLL, too.
- To some degree, similar problems occur for C code, too, where compilers have to agree on e.g. struct layout and calling conventions. Due to greater age and lower complexity, different C compilers are effectively compatible though. This is also accepted as a necessary feature in C as opposed to C++.

- 16,572
- 3
- 28
- 55