tags:

views:

90

answers:

3

Hi, I came across this article on Code Project that talks about using an abstract interface as an alternative to exporting an entire class from a C++ DLL to avoid name mangling issues. The author has a Release() method in his interface definition that is supposed to be called by the user to free the class object's resources after using it. To automate the calling of this method the author also creates an std::auto_ptr<T>-like class that calls the Release() method before deleting the object.

I was wondering whether the following approach would work instead:

#include <memory>

#if defined(XYZLIBRARY_EXPORT) // inside DLL
#   define XYZAPI   __declspec(dllexport)
#else // outside DLL
#   define XYZAPI   __declspec(dllimport)
#endif  // XYZLIBRARY_EXPORT

// The abstract interface for Xyz object.
// No extra specifiers required.
struct IXyz
{
    virtual int Foo(int n) = 0;

    //No Release() method, sub-class' destructor does cleanup
    //virtual void Release() = 0;

    virtual ~IXyz() {}
};

// Factory function that creates instances of the Xyz object.
// Private function, do not use directly
extern "C" XYZAPI IXyz* __stdcall GetXyz_();

#define GetXyz()      std::auto_ptr<IXyz>( GetXyz_() )

Of course, GetXyz() can be a global function defined in the header instead of a #define. The advantage to this method would be that we don't need to cook up our own derivative of auto_ptr that calls the Release() method.

Thanks for your answers, Ashish.

+3  A: 

by doing this, you risk calling delete (in your process, within auto_ptr's destructor) on an object that is not created by the matching call to new() (that is done inside the factory function, hence inside the dll). Trouble guaranteed, for instance when your dll is compiled in release mode while the calling process in debug mode.

The Release() method is better.

stijn
Thanks! I didn't think of the CRT's memory management models being different. Not only would it be a problem with debug and release versions, but as Greg Domjan has mentioned below it would be an issue if the DLL (or the caller) were linked statically to the CRT while the other was linked dynamically.I ended up using `std::tr1::shared_ptr` with a custom deleter that points to the `Release()` method.
Praetorian
+2  A: 

This is exactly how COM works. Avoiding re-inventing this wheel if you already target the Win32 API. Using smart pointers to store COM interface pointers is very common in Windows programming, their destructor calls the Release() method. Take a peek at the MSDN docs for _com_ptr_t and CComPtr for ideas.

Hans Passant
+1  A: 

The restriction you face if this is a public API is the CRT that the different modules will be linked against and that the CRT that creates the object also needs to be the one to delete it.

There will be a mess if you don't choose the right CRT Anything sharing memory management should use CRT (or other memory allocation) as external lib - ie. MSVC: Multi-threaded DLL (/MD)

Given that, then there is not even a need for the subclass to achieve your purpose.

Greg Domjan