From: Paul Bibbings on
Maxim Yegorushkin <maxim.yegorushkin(a)gmail.com> writes:

> On 15/02/10 04:55, Paul Bibbings wrote:
>> Specifically, I'm thinking here about passing pointers to standard
>> library objects across implementation boundaries (and, indeed, across
>> DLL boundaries).
>>
>> Let us say that we have a shared library, implemented in MSVC, that
>> exposes the following interface (pseudocode):
>>
>> // shared_lib_export.h
>> extern "C" {
>> __declspec(dllexport) std::ostream* __cdecl
>> GetNewStream(); // dynamically allocated
>> __declspec(dllexport) void __cdecl
>> PrintToStream(std::ostream*, const char *);
>> __declspec(dllexport) void __cdecl
>> FreeStream(std::ostream*); // deallocated
>> }
>
> What extern "C" does is that it turns off C++ name mangling because
> the mangling is different across different C++ compilers.
>
> If the compiler that includes this header file has a different
> mangling it is highly likely that it has incompatible C++ binary API
> and/or incompatible standard C++ library. Putting a C++ API into
> extern "C" turns off this safety net for no reason.
>
>> Then, in an application built using another implementation, say GNU gcc,
>> suppose I want to do something like:
>>
>> #include<ostream>
>> #include "shared_lib_import.h"
>>
>> int main()
>> {
>> std::ostream* os = GetNewStream();
>> PrintToStream(os, "Is this valid/safe/insane?\n");
>> FreeStream(os);
>>
>> return 0;
>> }
>>
>> What can we say about the validity of obtaining, storing and passing
>> back for use a pointer in this way?
>
> It won't work because MSVC and gcc have different standard
> libraries. MSVC std::ostream has one declaration, gcc std::ostream has
> another and they are not compatible.

<snip />

>> In short, can such a use ever be viable, and if so, under what
>> circumstances?
>
> Not in C++. You would need to wrap your API in C to make it truly portable.

Maxim, are you suggesting here that the possible differences in the
binary API - which we can suppose as being present for the sake of this
argument - have an effect here, when my usage is merely to store a *pointer*
and pass it back to the MSVC library to be used there? I am still
thinking this through (with much help from the responses here) but I
am beginning to feel confident that, aside from the issues of the
pointer itself, the differences in the binary API across the two
implementations should not of itself influence the viability of using the
pointer in the suggested way, although other factors may.

My confidence stems from the fact that /all/ uses of the object pointed
to - and that includes construction and destruction - occur within the
MSVC library code. Indeed there will exist there a definition and
implementation of std::ostream that is distinct from the one that would
be available on the user side were we to create and use our ostreams
there. But all we are accepting from the library call is a std::ostream
* and, assuming a sufficient compatibility of compliance with the
standard library requirements on that type, then our calling code
should, IMHO, have sufficient information to be able to store and
pass the pointer without needing to be aware of the specifics of the
object itself as it exists `over there', in the library code. Or am I
missing something?

Certainly the points you raise here would fit my own expectations were I
to imagine trying to `use' that pointer in the calling code, but that is
not in any sense what I am proposing to do here.

Regards

Paul Bibbings

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Bart van Ingen Schenau on
On Feb 18, 2:14 am, Paul Bibbings <paul.bibbi...(a)gmail.com> wrote:
>
> My confidence stems from the fact that /all/ uses of the object pointed
> to - and that includes construction and destruction - occur within the
> MSVC library code. Indeed there will exist there a definition and
> implementation of std::ostream that is distinct from the one that would
> be available on the user side were we to create and use our ostreams
> there. But all we are accepting from the library call is a std::ostream
> * and, assuming a sufficient compatibility of compliance with the
> standard library requirements on that type, then our calling code
> should, IMHO, have sufficient information to be able to store and
> pass the pointer without needing to be aware of the specifics of the
> object itself as it exists `over there', in the library code. Or am I
> missing something?

No, you are fully correct.
However, to take away any temptation on the user of the library, I
would recommend that you pass a pointer to an incomplete type.
Otherwise the user might be tempted to access the members of the
object directly, because the type seems to match with a well-known
type.
If you pass a pointer to an incomplete type, even if that type
inherits from std::ostream in the implementation, the user can't make
any unwarrented assumptions.

>
> Regards
>
> Paul Bibbings
>
Bart v Ingen Schenau


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]