Boost logo

Boost :

Subject: [boost] [smart_ptr] enable_shared_from_this and multiple inheritance solution
From: Philippe Cayouette (pcayouette_at_[hidden])
Date: 2010-11-24 20:22:15


Fellow boosters,

A little while ago I found a pernicious problem with
enable_shared_from_this when used with multiple inheritance.

Here is an example:

#include <boost/smart_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

class Base1 : public boost::enable_shared_from_this< Base1 >
{
public:
   Base1() {}
   virtual ~Base1() {}
   boost::shared_ptr< Base1 > GetBase1()
   {
     return shared_from_this();
   }
};

class Base2 : public boost::enable_shared_from_this< Base2 >
{
public:
   Base2() {}
   virtual ~Base2() {}
   boost::shared_ptr< Base2 > GetBase2()
   {
     return shared_from_this();
   }
};

class Derived : public Base1, public Base2
{
};

int main()
{
   boost::shared_ptr< Derived > wDerived(new Derived());
   // Either of the following lines crashes with GCC, but the first one
   // passes on VS2005
   boost::shared_ptr< Base1 > wBase1 = wDerived->GetBase1();
   boost::shared_ptr< Base2 > wBase2 = wDerived->GetBase2();
   return 0;
}

Since Derived inherits from two bases that each inherits from
enable_shared_from_this, it has two internal weak_ptr. These weak_ptr
are usualy initialised in the constructor of the shared_ptr and I have
observed two different behavior with the above code depending if I build
it with VS2005 or GCC. With VS2005, the first base class in the
inheritance list of Derived (here Base1) get its internal weak_ptr
initialized, but not the second one, thus it crashes when we try to
access the Base2 weak_ptr via shared_from_this. On GCC, neither base
classes get their internal weak_ptr initialized and both base classes
access to their internal weak_ptr provokes a crash.

To prevent that kind of problem, I created a SmartPtrBuilder and a
(very) slightly modified EnabledSharedFromThis (only one assert was
removed).

Here is the same program with the modifications:

#include <boost/smart_ptr.hpp>
#include "EnableSharedFromThis.h"
#include "SmartPtrBuilder.h"

class Base1 : public Generic::EnableSharedFromThis< Base1 >
{
public:
   Base1() {}
   virtual ~Base1() {}
   boost::shared_ptr< Base1 > GetBase1()
   {
     return SharedFromThis();
   }
};

class Base2 : public Generic::EnableSharedFromThis< Base2 >
{
public:
   Base2() {}
   virtual ~Base2() {}
   boost::shared_ptr< Base2 > GetBase2()
   {
     return SharedFromThis();
   }
};

class Derived : public Base1, public Base2
{
public:
   // Factory method
   static boost::shared_ptr< Derived > Create()
   {
     return Generic::SmartPtrBuilder::CreateSharedPtr< Base1, Base2
>(new Derived());
   }
private:
   Derived() {}
};

int main()
{
   boost::shared_ptr< Derived > wDerived = Derived::Create();
   // No more crashes
   boost::shared_ptr< Base1 > wBase1 = wDerived->GetBase1();
   boost::shared_ptr< Base2 > wBase2 = wDerived->GetBase2();
   return 0;
}

As you can see, Derived has now a factory method that uses the
SmartPtrBuilder to create the shared_ptr. Two template arguments are
used on CreateSharedPtr, allowing it to initialize the internal weak_ptr
of each base classes. The SmartPtrBuilder I did can support up to 10
base classes as template arguments and I also did a variadic template
version which has no limit.

By using the factory method, the users of Derived don't need to know
which of its base classes need its internal weak_ptr need to be initialized.

Source code for the SmartPtrBuilder and the modified EnableSharedFromThis:
http://spoluck.ca:8000/boost/EnableSharedFromThis.h
http://spoluck.ca:8000/boost/SmartPtrBuilder.h

That builder might be incorporated to the boost smart pointer library so
that anyone could use its facilities to prevent the problem mentioned
earlier. No modification to the mighty shared_ptr class required, only
the addition of a builder.

What do you think?

Philippe


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk