|
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