
Philippe Cayouette <pcayouette@spoluck.ca> 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]
(new Derived()); }
class Derived : public Base1, public Base2 { public: // Factory method static boost::shared_ptr< Derived > Create() { return Generic::SmartPtrBuilder::CreateSharedPtr< Base1, Base2 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