Question

I'm currently remodelling a dynamic library project and removed a lot of STL and other dependencies from its header resulting in conflicting implementations between different toolchains.

The DLL Interface now only contains plain C-types and custom types whom implementation depends solely on the DLL code. But now I'm stuck with two remaining points.

  1. The DLL should return some sort of reference counting smart pointer (with a weak pointer option)
  2. Managing construction and destruction between the DLL boundaries. This more or less looks like this.

.

#include <memory>

class Config;
typedef std::shared_ptr<Config> ConfigPtr;

class CONFIGAPI_DLL Config
{
    public:
    ///...
    ConfigPtr getNewNode( char const* query = "." )
    {
        // construct obj in dll, but construct shared_ptr in header
        return ConfigPtr( _constructgetNewNode(query) );
    }
    ConfigPtr getNewNodeFromHeader( char const* query = "." )
    {
        // construct obj and shared_ptr in header
        auto obj = new Config;
        obj->_init(query);
        return ConfigPtr( obj );
    }


    private:
    Config* _constructNewNode( char const* query = "." );
    void _init( char const* query = "." );
};

I was thinking by creating the share_ptr at the dll headers (getNewNode) it would prevent mixed implementations of std::shared_ptr ...but I don't know if that is a good idea?

But I also construct the new object inside the DLL (_constructNewNode) and this means it should also be destructed there?

I tried to construct it in the headers and so in the user code scope (getNewNodeFromHeader)... this shouldn't cause problems?

The downside is I still export the C++11 header and rule out all older compilers. Is it possible to export the shared_ptr type from the dll as unconflicting code but still be compatible with std::shared_ptr?

thanks!

Was it helpful?

Solution

I suppose you want to mix creation and destruction of the objects (Config and the shared_ptr). If the DLL client is not compiled with the same version and configuration of the compiler you would be in troubles (for example, mixing debug and release modules). The main problem I see here: there is not any type of standard ABI for C++.

I was thinking by creating the share_ptr at the dll headers (getNewNode) it would prevent mixed implementations of std::shared_ptr ...but I don't know if that is a good idea?

If you define your header file to create the shared_ptr, that would be OK if only a module use that header file. I mean, if the shared_ptr is not to be used for the content of the DLL, for example. BUT if other client module (a binary module, like other DLL) use also that header, you must be sure they are compiled with the same compiler and compiling configuration. If you have not that guarantee, then is not good idea.

But I also construct the new object inside the DLL (_constructNewNode) and this means it should also be destructed there?

If you construct the object inside the DLL, you'll be better destroying inside the DLL. How to do that? specifying a deleter at shared_ptr construction. Something like

struct ConfigDeleter {
    void operator()(Config* c) {
        c->destroy(); // or as you want to implement it.
};
typedef std::shared_ptr<Config, ConfigDeleter> ConfigPtr;

I tried to construct it in the headers and so in the user code scope (getNewNodeFromHeader)... this shouldn't cause problems?

As before, it depends if you can guarantee all modules are homogeneous (same compiler version and configuration, library, etc.). But of you want to implement as that, do it well:

// construct obj and shared_ptr in header
auto obj_ = make_shared<Config>();
obj_->init(query);

It is exception-safe and more efficient: just one allocation to store the object and the reference, instead two allocations in your sample.

If you want your code be safe for mixing modules, implement all allocations (shared_ptr included) into the DLL, if they are shared. Exports a "C" interface, and create header file to wrap that interface in classes. Something like:

class WConfig {
public:
    WConfig(): m(IDll->create()) {
    }
    // other member functions as interface stub
    ~WConfig() {
        IDll->release(m);
    }
private:
    Config* m;
};

To share this object, you can use copy constructor (that copy the pointer and call to IDll->reference(m), for example) or any other approach.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top