From: Paul Bibbings on 14 Feb 2010 11:55 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 14 Feb 2010 15:12 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 14 Feb 2010 15:11 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 15 Feb 2010 05:42 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 15 Feb 2010 05:47
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! ] |