From: Paul Bibbings on
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
}

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? Can this ever be well-defined, and
what are the minimal guarantees that we must require for it to be so
(not to mention passing across the C-style string)?

For instance, I'm guessing that the size of a pointer in both
implementations would need to be the same. Also, we have to allow the
shared library code to manage the destruction of the object pointed to,
ensuring its valid lifetime by a properly-balanced use of GetNewStream()
and FreeStream(...). More importantly, we would have to forget about
any attempts to use the object in our calling code, in the sense of
dereferencing the pointer.

In short, can such a use ever be viable, and if so, under what
circumstances? Does the standard have anything to say to help me 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: Martin B. on
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
> }
>
> Then, in an application built using another implementation, say GNU gcc,
> suppose I want to do something like:
>
> #include <ostream>
>(....)
> std::ostream* os = GetNewStream();
> PrintToStream(os, "Is this valid/safe/insane?\n");
> FreeStream(os);
>(....)
> What can we say about the validity of obtaining, storing and passing
> back for use a pointer in this way? (...)
>
> For instance, I'm guessing that the size of a pointer in both
> implementations would need to be the same.

I'm not so sure about that. I'm not saying a 64bit-DLL could interface
with a 32bit-DLL, but correctly handling (or erroring out) the size of
integral-C-types should be the responsibility of the dll loading
mechanism. That is, I suspect that the export symbol for a 32bit pointer
and a 64bit pointer is different and so if the pointer sizes are
different you are not even allowed to call the exported function. (The
problem persists of course if you use dynamic loading with LoadLibary)

> (...) More importantly, we would have to forget about
> any attempts to use the object in our calling code, in the sense of
> dereferencing the pointer.
>

Yes. And for that reason you should not have used #include <ostream>.
(Maybe #include <iosfwd> ?)

From my experience it is completely OK to pass around pointers like
that, but it might make more sense to use pointers that either
a) are not dereferenceable because the client side doesn't know about
the implementation or
b) use pointers that are safely dereferenceable.

br,
Martin

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

From: Nick Hounsome on
On 15 Feb, 04:55, Paul Bibbings <paul.bibbi...(a)gmail.com> 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
> }
>
> 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? Can this ever be well-defined, and
> what are the minimal guarantees that we must require for it to be so
> (not to mention passing across the C-style string)?

Strictly we can't say anything at all.
The std defines a single implementation. It has nothing to say about
the interaction between 2 implementations.

>
> For instance, I'm guessing that the size of a pointer in both
> implementations would need to be the same.
> Also, we have to allow the
> shared library code to manage the destruction of the object pointed to,
> ensuring its valid lifetime by a properly-balanced use of GetNewStream()
> and FreeStream(...). More importantly, we would have to forget about
> any attempts to use the object in our calling code, in the sense of
> dereferencing the pointer.

You're missing the most important problems:

1) __declspec is not C++ and so no implementation is obliged to accept
it. The std has nothing to say about libraries at all let alone
dynamic ones.
2) __cdecl is not C++ either and this affects the parameter passing
and stack management conventions which are not defined by the standard
so you can't even portably call any function between implementations.

> In short, can such a use ever be viable, and if so, under what
> circumstances? Does the standard have anything to say to help me here?

No. The std cannot and will not ever say anything about this.

In practical terms every implementation, as a matter of good business,
will provide a method or compiler hack to interface to the platform OS
so I would expect it to work with any compiler targeted at Windows on
a particular processor family.


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

From: Maxim Yegorushkin on
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.

It will work with the same C++ compiler though.

> Can this ever be well-defined, and
> what are the minimal guarantees that we must require for it to be so
> (not to mention passing across the C-style string)?

Only if the compilers in question agree on the C++ binary API, as Linux
versions of gcc and Intel do.

> For instance, I'm guessing that the size of a pointer in both
> implementations would need to be the same.

Pointer sizes are most often defined by platform C binary API, so that
all compilers for that platform must obey it (otherwise they won't be
able to call platform/kernel functions that accept pointers).

> Also, we have to allow the
> shared library code to manage the destruction of the object pointed to,
> ensuring its valid lifetime by a properly-balanced use of GetNewStream()
> and FreeStream(...). More importantly, we would have to forget about
> any attempts to use the object in our calling code, in the sense of
> dereferencing the pointer.
>
> 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.

--
Max

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

From: Goran on
On Feb 15, 5:55 am, Paul Bibbings <paul.bibbi...(a)gmail.com> wrote:

> In short, can such a use ever be viable

Theoretically, yes.

> and if so, under what circumstances?

I think, pretty much under "two toolchains were made to work together"
circumstances.

> Does the standard have anything to say to help me here?

Standard says nothing about "modules" as we know them in all-rounder
OS-es like Unix or Windows. That goes for C++ and C. Due to higher
level concept that are present in C++ (e.g. virtual calls and
overloading), interoperability between C++ tool chain implementations
is non-existent.

You should lay off this idea and use, instead, either a pure C
interface or some integration technology. Note that C interface works
mostly by accident: because underlying system usually defines it's own
interface in terms understandable by C compiler, everyone has to
adhere to it.

Goran.


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