|
Boost : |
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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk