0

This is a continuation of my previous question here. A little introduction:

"I am developing a library which has two layers, unmanaged (C++) and managed (C++/CLI). The unmanaged layer contains the logics and the computation algorithms, while the managed layer provides interface and visualisation to a .NET-based host application. A class in the managed layer wraps its class counterpart in the unmanaged layer, e.g. ManagedA wraps UnmanagedA and ManagedB wraps UnmanagedB."

For clarity, these two layers are implemented as separate DLLs.

In the previous post I asked about wrapping the same unmanaged object in multiple managed objects, which can be done using std::shared_ptr. However, the solution that I wrote (snippet below), based on a reply to the original question, has a constructor with a shared_ptr argument, which assumes that the C++ DLL passes an std::shared_ptr instance.

My question is: is it recommended to pass a std::shared_ptr across different DLLs? A commentator here does not recommend doing so because of potential compatibility issues. However this was written in 2009, so I am wondering if there has been a new argument in favour of doing this.

Edit for elaboration: The next question is, if not recommended, what would be the alternative? The problem is, if UnmanagedA::B() returns a raw pointer, ManagedB will treat it as a new instance which has not been shared yet. Therefore, when one of two ManagedB instances go out of scope, the UnmanagedB instance will be deleted, and this invalidates the pointer inside the other ManagedB instance. Many thanks.

// Unmanaged classes in a C++ DLL

class UnmanagedB
{
public:
    UnmanagedB() {}
    ~UnmanagedB() {}

    int i = 0;
};

class UnmanagedA
{
public:
    UnmanagedA(UnmanagedB* pUnmanagedB)
    : m_pUnmanagedB(pUnmanagedB)
    {
    }

    ~UnmanagedA() {}

    std::shared_ptr<UnmanagedB> B() { return m_pUnmanagedB; }

protected:
    std::shared_ptr<UnmanagedB> m_pUnmanagedB;
};

// Managed classes in a C++/CLI DLL
public ref class ManagedA : IDisposable
{
public:
    ManagedA(UnmanagedA* pUnmanagedA)
        : m_pUnmanagedA(pUnmanagedA)
    {

    }

    ~ManagedA()
    {
        delete m_pUnmanagedA;
    }

    ManagedB^ B()
    {
        return gcnew ManagedB(m_pUnmanagedA->B());
    }

private:
    UnmanagedA* m_pUnmanagedA;
};

public ref class ManagedB : IDisposable
{
public:
    ManagedB(const std::shared_ptr<UnmanagedB>& rkUnmanagedB)
        : m_pUnmanagedB(new std::shared_ptr<UnmanagedB>(rkUnmanagedB))
    {
    }

    ~ManagedB()
    {
        delete m_pUnmanagedB;
    }

private:
    std::shared_ptr<UnmanagedB>* m_pUnmanagedB;
    std::shared_ptr<UnmanagedB> GetPtr() { return *m_pUnmanagedB; }
};
  • *Is it recommended to pass a std::shared_ptr across different DLLs?*-- No. – PaulMcKenzie Mar 26 '18 at 01:43
  • Thank you, but I should have elaborated more. What would be the alternative? The problem is, if UnmanagedA::B() returns a raw pointer, ManagedB will treat it as a new instance which has not been shared yet. Therefore, when one of two ManagedB instances go out of scope, the UnmanagedB instance will be deleted, and this invalidates the pointer inside the other ManagedB instance. – John Atangwa Mar 26 '18 at 05:10
  • 1. You szenario isn't complete. Who returns the pointer? The DLL? 2. If the pointer is created inside the DLL a delete will only work if both components (DLL and EXE/DLL) use the same CRT. 3. The link you gave shows an answer using a smart pointer (currently highest vote) 4. If all components are compiled together and distributed together I see no problem to use a smart pointer at all.passing smart pointers between DLL and EXE all the time. But all components are always distributed and compiled together. – xMRi Mar 26 '18 at 07:25
  • Sharing standard C++ library template types across a DLL boundary is in general pretty troublesome. You have to make sure that both DLLs use the same library so the object layout is compatible. That in general requires using the exact same C++ compiler version with the exact same settings. std::shared_ptr isn't the worst btw, they did not have a lot of reason to mess with it. Something like std::vector is not even compatible with the same compiler, the build flavor matters. If you can ensure that these DLLs are always built at the same time and deployed together then it is not a problem. – Hans Passant Mar 26 '18 at 07:41
  • The libraries will be open source and will be released as a framework, but we aim that the users can extend and adopt the C++ library in any client software e.g. by writing their own .NET library that wraps it, or using it as an API. Would it be alright to keep the functions returning STL data types with an instruction that the user needs to (re-)build the library with their own compiler and configuration? – John Atangwa Mar 26 '18 at 08:11
  • @xMRi 1 & 2. The unmanaged instances are created in the C++ layer, but stored and currently deleted in the C++/CLI layer. Would you suggest that the deletion is moved to the C++ layer too, as you hinted in 3, or is it alright considering my above comment? – John Atangwa Mar 26 '18 at 08:12
  • This doesn't matter were the object is deleted. I use the same scenario. Create a object in C++ deliver the pointer into C++/CLI, and it gets deletedthere, when it goes out of scope. I also use much more complex things inclusive weak_ptr... – xMRi Mar 26 '18 at 08:16
  • "It doesn't matter" as long as all modules are sharing the same CRT and compiled together... Again read the answers of the question you mentioned. – xMRi Mar 26 '18 at 08:46

0 Answers0