I want to be able to return from a function inside a DLL a std::string
by value:
1) std::string func();
In another module (DLL or exe) I want to use this even if I use
different c runtime linkages (example: func() inside the DLL uses
dynamic c runtime and the other module uses the same c runtime but
"static")
The way I have done successfully this when passing std::strings as
references inside a DLL was to make sure I do not resize by any means
the capacity of the string inside the DLL( so as to not delete
afterwards the string when memory was allocated inside another module,
the DLL)
Case 1 seems to work fine on my complier (Visual Studio) although I
don't really understand why ...
2) Is there any other better alternative because I don't want to stick
on using raw pointers when crossing DLL boundaries ?
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
AFAIK, there's no guarantee whatsoever that this will work in C++. C++
simply doesn't specify any means of connecting two executables. There
are efforts to specify C++ ABI ("B" for binary), but AFAIK, not on
Windows.
What will work in practice, I think, is to link both executables to
the same _executable_ (*.dll, *.so) runtime version.
BTW, you are on Windows. Why not use COM? It's a PITA, but quite
capable for stable binary interfaces.
HTH,
Goran.
The problem is caused by the mismatch of allocation and deallocation
functions (new/delete, malloc/free) from different C++ run-time. To
solve it, you need to make sure that objects are allocated and
deallocated by functions from the same C++ run-time. The easiest way
to do so it to allocate and deallocate objects in the same source
file.
Applying this to std::string and std containers, what you need to do
is to use a custom allocator. That allocator will need to be in one
dll, so that allocator::allocate() and allocator::deallocate() use the
same C++ run-time.
E.g.:
// these two functions are implemented in one dll
__declspec(dllexport) void* dll_malloc(size_t);
__declspec(dllexport) void dll_free(void*);
template<class T>
struct dll_allocator
{
// copy-intellegent-paste std::allocator implementation here
// the only change is in the following two functions
pointer allocate(size_type n, const void* = 0)
{
if(void* p = dll_malloc(sizeof(T) * n))
return p;
throw std::bad_alloc();
}
void deallocate(pointer p, size_type)
{
dll_free(p);
}
};
// now use dll_string between dll's boundaries
typedef std::basic_string<
char
, std::char_traits<char>
, dll_allocator<char>
> dll_string;
// an example of dll_string_vector
typedef std::vector<
dll_string
, dll_allocator<dll_string>
> dll_string_vector;
--
Max
Don't do this.
You have two choices:
a) All modules are compiled(!!) and linked with the same options and
runtime libraries.
b) Use a custom string-buffer class to pass the string between module
boundaries and define/implement *this* class in a separate DLL that all
other modules link against.
ad a) ... you need to make sure that the std::string in your DLL is the
same thing as the std:string in your exe. Optimization and
security/debug settings may influence the size and layout of class objects.
... Different runtime libraries will mean heap problems when std::string
is freeing it's character memory.
ad b) ... Since you define this class yourself and take care all modules
always use the same version there's no binary layout problem.
... Since the class memory operations are in your custom DLL there will
be no HEAP mismatch problems.
br,
Martin
A simple way to achieve this is to use objects with virtual
destructors.
-Le Chaud Lapin-
I don't think that this will solve the problems.
If a DLL function returns a Obj* and we later have to delete pObj then
this will resolve the correct dtor/free functions.
However, what if - as was the case in the example - the DLL function
returns Obj by value and each module uses it's own compiled+linked
version of the Obj class. (as was implied by the OP whe he used
std::string with different runtime libraries)
In this case we have again a mismatch btw the used dtor and ctor for the
returned object. And the virtual dtor does not help as far as I could
determine.
cheers,
Martin
There are several issues with such an approach.
One is that it is not applicable to std:: classes because most of them
don't have virtual destructors.
Another one, is that it is a Microsoft-specific hack. I.e. nothing in
the standard guarantees that the deallocation function will be called
from the same translation unit as the unit where the virtual
destructor is defined. (speculation: a virtual destructor could
internally return a pointer to the beginning of the object storage,
which delete expression would pass to operator delete() from the
current translation unit).
Instead, the desired effect can be achieved using standard and
portable means:
* Custom allocator for std:: containers.
* Class-specific overloaded new/delete, no virtual destructor
required, works well with smart-pointers.
* Factory functions matched with custom destroy function (i.e. Foo*
fooCreate() / void fooDestroy(Foo*)), works well with smart-pointers.
--
Max