Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

returning std::string by value across DLL boundaries

1,440 views
Skip to first unread message

ghita

unread,
Jun 5, 2009, 1:54:23 PM6/5/09
to
Hi !

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! ]

Goran

unread,
Jun 7, 2009, 3:18:24 AM6/7/09
to
> 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")

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.

Maxim Yegorushkin

unread,
Jun 8, 2009, 1:16:06 AM6/8/09
to
On 5 June, 18:54, ghita <gheorghe.mari...@googlemail.com> wrote:
> Hi !
>
> 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 ?

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

Martin T.

unread,
Jun 8, 2009, 7:10:37 PM6/8/09
to
ghita wrote:
> Hi !
>
> 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")
> <snip>

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

Le Chaud Lapin

unread,
Jun 8, 2009, 7:12:45 PM6/8/09
to
On Jun 8, 12:16 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
wrote:
[snip]

> 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.
[snip]

A simple way to achieve this is to use objects with virtual
destructors.

-Le Chaud Lapin-

Martin T.

unread,
Jun 9, 2009, 9:56:50 PM6/9/09
to
Le Chaud Lapin wrote:
> On Jun 8, 12:16 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
> wrote:
> [snip]
>> 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.
> [snip]
>
> A simple way to achieve this is to use objects with virtual
> destructors.
>

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

Maxim Yegorushkin

unread,
Jun 9, 2009, 10:00:33 PM6/9/09
to
On Jun 9, 12:12 am, Le Chaud Lapin <jaibudu...@gmail.com> wrote:
> On Jun 8, 12:16 am, Maxim Yegorushkin <maxim.yegorush...@gmail.com>
> wrote:
> [snip]> 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.
>
> [snip]
>
> A simple way to achieve this is to use objects with virtual
> destructors.

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

0 new messages