boost shared_from_this and multiple inheritance

ChrisPeterson picture ChrisPeterson · Feb 18, 2013 · Viewed 6.9k times · Source

I am currently having some troubles when using boost enable_shared_from_this and multiple inheritance.

The scenario can be described as follows:

  1. Class A implements some functionality and should inherit from enable_shared_from_this

  2. Class B implements another functionality and should inherit from enable_shared_from_this

  3. Class D inherits functionalities from A and B (class D : public A, public B {})

  4. When using some class B functionality from class D I got an exception (bad_weak_ptr)

  5. To inherit enable_shared_from_this from class D is not an option for me

I am not sure about how to solve this.

Oh, I am using Visual C++ 2010.

Answer

Dave S picture Dave S · Feb 18, 2013

Expanding on Potatoswatter's solution, if you can change A and B to use a something slightly different than enable_shared_from_this. The code uses the standard library versions, but the boost implementation should be similar. Explanation below

#include <memory>
template<typename T>
struct enable_shared_from_this_virtual;

class enable_shared_from_this_virtual_base : public std::enable_shared_from_this<enable_shared_from_this_virtual_base>
{
    typedef std::enable_shared_from_this<enable_shared_from_this_virtual_base> base_type;
    template<typename T>
    friend struct enable_shared_from_this_virtual;

    std::shared_ptr<enable_shared_from_this_virtual_base> shared_from_this()
    {
        return base_type::shared_from_this();
    }
    std::shared_ptr<enable_shared_from_this_virtual_base const> shared_from_this() const
    {
       return base_type::shared_from_this();
    }
};

template<typename T>
struct enable_shared_from_this_virtual: virtual enable_shared_from_this_virtual_base
{
    typedef enable_shared_from_this_virtual_base base_type;

public:
    std::shared_ptr<T> shared_from_this()
    {
       std::shared_ptr<T> result(base_type::shared_from_this(), static_cast<T*>(this));
       return result;
    }

    std::shared_ptr<T const> shared_from_this() const
    {
        std::shared_ptr<T const> result(base_type::shared_from_this(), static_cast<T const*>(this));
        return result;
    }
};

So, the intent is that struct A would inherit publicly from enable_shared_from_this_virtual<A> and struct B would inherit publicly from enable_shared_from_this_virtual<B>. Since they both share the same common virtual object, there is only one std::enable_shared_from_this in the hierarchy.

When you call either classes shared_from_this, it performs a cast from enable_shared_from_this_virtual<T>* to T*, and uses the aliasing constructor of shared_ptr so that the new shared_ptr points to T* and shares ownership with the single shared pointer.

The use of the friend at the top is to prevent anyone from accessing the shared_from_this() members of the virtual base directly. They have to go through the ones provided by enable_shared_from_this_virtual<T>.

An example:

#include <iostream>

struct A : public enable_shared_from_this_virtual<A>
{
    void foo()
    {
        shared_from_this()->baz();
    }

    void baz()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct B : public enable_shared_from_this_virtual<B>
{
    void bar()
    {
        shared_from_this()->baz();
    }

    void baz()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};

struct D: A, B {};


int main()
{
 std::shared_ptr<D> d(new D);
 d->foo();
 d->bar();
 return 0;
}