$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
Subject: Re: [boost] [smart_ptr] enable_shared_from_this and multiple inheritance solution
From: Ansel Sermersheim (agserm_at_[hidden])
Date: 2010-11-28 15:02:50
Philippe Cayouette <pcayouette_at_[hidden]> writes:
> Fellow boosters,
>
> A little while ago I found a pernicious problem with
> enable_shared_from_this when used with multiple inheritance.
>
> [snip detailed example]
>
> 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.
I have also run into this problem. In my particular example, I was
able to avoid the problem by moving the inheritance from
enable_shared_from_this up the class hierarchy.
However, the experience lead me to consider alternate ways to resolve
this issue. I was revisiting this work the other day, and I thought I
would take this opportunity to share my solution as well.
> 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:
>
> [snip]
>
> 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.
This is a really neat idea, and I never considered using template
arguments to resolve this issue of multiple inheritance.
> 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.
I took a slightly different tack, not wanting to force users to make
use of a factory method. I don't mind factory methods, but I hesitate
to impose the use of a factory for no other reason than implementation
details.
In my solution, my enable_shared_from_polymorphic<T> class is just a
shim that inherits virtually from enable_shared_from_polymorphic_base,
which is not a template type. The base class holds a weak pointer,
which can be dynamic_pointer_cast by the child class when needed.
This allows your example to work as expected by merely inheriting from
enable_shared_from_polymorphic<T> rather than
enable_shared_from_this<T>.
If anyone is interested in the code, it can be found at:
http://208.106.110.44/~ansel/boost/enable_shared_from_polymorphic.patch
This patch is against the current SVN version of boost.
The pro of this method is that the class can be used identically to
the way that enable_shared_from_this is used. A downside is that any
class deriving from enable_shared_from_polymorphic<T> becomes
polymorphic and incurs all the cost thereof.
This cost could potentially be mitigated if these classes used
static_pointer_cast instead of dynamic_pointer_cast. However, I am not
familiar enough with the appropriate areas of the C++ standard to be
sure that would work in cases of multiple inheritance, precisely when
these classes are useful.
Thanks for reading,
Ansel