Re: two bases with shared_from_this?
limcore@gazeta.pl wrote:
#include <string>
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace std;
using namespace boost;
struct cA1 : enable_shared_from_this<cA1> {
string name;
cA1(string n) : name(n) {}
virtual ~cA1() {}
virtual void Hello() { cout<<"I am " << name << " type " << Type()
<< endl; }
virtual string Type() { return "cA1"; }
virtual void foo() { cout<<"Foo 1 nothing"<<endl; }
} ;
struct cA2 : enable_shared_from_this<cA2> {
string name;
cA2(string n) : name(n) {}
virtual ~cA2() {}
virtual void Hello() { cout<<"I am " << name << " type " << Type()
<< endl; }
virtual string Type() { return "cA2"; }
virtual void foo() { cout<<"Foo 2 nothing"<<endl; }
} ;
struct cB : public cA1, public cA2 {
cB(string n):cA1(n+"1"),cA2(n+"2"){}
virtual string Type() { return "cB"; }
};
struct cC : public cB {
cC(string n):cB(n) {}
virtual string Type() { return "cC"; }
virtual void foo() {
shared_ptr<cA1> ptr = cA1::shared_from_this(); // <------
ptr->Hello();
}
};
int main() {
shared_ptr<cC> c( new cC("xxx") );
c->foo();
}
I decided to use your second example since it is straightforward to
see what is going on. Unfortunately, my conclusion is you will
ultimately have to solve this through rearchitecting your class
hierarchy based on what I uncovered. (ie. I don't know how to
directly solve your problem since I do not know your requirements)
So here is what I tried:
Using gcc344 and boost 1.33.1, I compared a trace of the program
when cC just derived from enable_shared_from_this and how you
wrote it. The main difference that I saw is this. When it was working,
(ie. when cC just derived from enable_shared_from_this directly),
the object construction went to shared_ptr.hpp:86. This then triggered
proper _internal_weak_pointer construction. In your second example,
weirdly enough, it went to shared_ptr.hpp:91. If you look at the
difference, one will trigger proper counting, whereas the other has
an empty body!! This is what is causing you to get the throw.
Now why exactly is this happening. I couldn't directly figure this out
but I decided to use gc402 tr1 memory and substituted their shared
pointer in your example. Here is what I got:
main.cpp:52: instantiated from here
/opt/gcc402/lib/gcc/i686-pc-linux-gnu/4.0.2/../../../../include/c++/4.0.2/tr1/boost_shared_ptr.h:499:
error: call of overloaded
???__enable_shared_from_this(std::tr1::shared_count&, cC*&, cC*&)??? is
ambiguous
/opt/gcc402/lib/gcc/i686-pc-linux-gnu/4.0.2/../../../../include/c++/4.0.2/tr1/boost_shared_ptr.h:461:
note: candidates are: void std::tr1::__enable_shared_from_this(const
std::tr1::shared_count&, ...)
/opt/gcc402/lib/gcc/i686-pc-linux-gnu/4.0.2/../../../../include/c++/4.0.2/tr1/boost_shared_ptr.h:946:
note: void std::tr1::__enable_shared_from_this(const
std::tr1::shared_count&, const std::tr1::enable_shared_from_this<_Tp>*,
const _Tp1*) [with _Tp1 = cC, _Tp = cA1]
/opt/gcc402/lib/gcc/i686-pc-linux-gnu/4.0.2/../../../../include/c++/4.0.2/tr1/boost_shared_ptr.h:946:
note: void std::tr1::__enable_shared_from_this(const
std::tr1::shared_count&, const std::tr1::enable_shared_from_this<_Tp>*,
const _Tp1*) [with _Tp1 = cC, _Tp = cA2]
Now, the implementations of tr1/memory and boost is not identical but
similar enough to probably point out the problem you are encountering.
Basically, what this error is telling me is that shared_ptr internally
is confused as to how to construct the weak pointer since there are
two of them that are viable. I am guessing that in boost 1.33.1, the
confusion is eliminated by inserting shared_ptr.hpp:91 BUT this causes
major problems.
Here are two suggestions: (a) preferred (b) if you have no alternative
a) Use intrusive pointers instead. Since the count and counting
mechanism is controlled by you the user and is embedded inside
your object instead. There is no requirement to use weak pointers
and there will be no ambiguity (by your proper design). Intrusive
pointers also can "get a ref counted pointer from itself" by virtue
of the fact that the count is embedded in the object.
Check out:
<boost/intrusive_ptr.hpp>
for more info on this.
b) in your constructors (such as for cC, cB or both) you will need
to perform manual proper construction of the weak pointer yourself.
Since you are deriving from this:
template<class T> class enable_shared_from_this
{
public:
[..snip..]
typedef T _internal_element_type; // for bcc 5.5.1
mutable weak_ptr<_internal_element_type> _internal_weak_this;
};
you can perform manual initialization of the public _internal_weak_this
pointer in your constructor. This will solve your problems also but I
honestly think it is quite dangerous/tedious to do so.
HTH!!