Topic: What is the rationale behind std::tr1::enable_shared_from_this?


Author: "Javier" <ljestrada@developeer.com>
Date: Wed, 13 Dec 2006 12:42:38 CST
Raw View
I'm trying to find the rationale behind this class beyond the Boost
documentation
(http://www.boost.org/libs/smart_ptr/sp_techniques.html#from_this) and
Pete Becker's C++ Standard Library Extensions book (section 2.6, pp
57).

I understand the technicalities of the class, but what are some
practical uses?  Are there any "gotchas"?  It would seem to me that
once a class is "smartified" there are implications in a design
regarding its lifetime.

Regards,

Javier

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Mathias Gaunard <loufoque@remove.gmail.com>
Date: Thu, 14 Dec 2006 14:25:48 CST
Raw View
Javier wrote:

> I understand the technicalities of the class, but what are some
> practical uses?  Are there any "gotchas"?  It would seem to me that
> once a class is "smartified" there are implications in a design
> regarding its lifetime.

I know it's used in the examples of Boost.asio.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Wed, 20 Dec 2006 22:24:06 CST
Raw View
A regular "reference counted shared pointer" only allows an acyclic
dependency graph. This is fairly common knowledge. If we need cyclic
references, such as back pointers in a tree type graph (like a file
hierarchy -- all directories have a reference to their entries, and all
entries have a reference to their directory. This is cyclic) or a
doubly linked list. In this case we would use a shared_ptr in one
direction, and a weak_ptr for the other . There is no apriori
preference which is better to use in which "direction." Either way is
fine, as long as it is consistent for your project.
To the point: There are many cases where we would want a reflexive
dependency graph. In other words, an object can issue its own
shared_ptr. This is the semantic "whatever shared_ptr manager owns me,
give me a shared_ptr attached to him." This is EXTREMELY useful in a
design where a single object implements multiple interfaces. i.e
struct Interface2{};
struct Interface1{
   virtual tr1::shared_ptr<Interface2> get()=0;
};
//a factory for interface 1
tr1::shared_ptr<Interface1> make(int a){
if (a)//first choice
    return shared_ptr<Impl>(new Impl);
//else
return shared_ptr<Impl2>(new Impl2);
}

//the implementations, hidden from user
class Impl:Interface2,Interface1,
tr1::enable_shared_from_this<Impl>{

     friend shared_ptr<Interface1> make(int);//factory pattern
     Impl(){}
public:
     tr1::shared_ptr<Interface2> get(){
         return shared_from_this();
}
};
//without enable_shared_from_this, we need separate classes
class Privateimpl:Interface2{
friend class Impl2;
Privateimpl(){}
};
class Impl2:Interface1,tr1::enable_shared_from_this<Impl>{

     friend shared_ptr<Interface1> make(int);//factory pattern
     Impl2(){}
public:

     tr1::shared_ptr<Interface2> get(){
         return tr1::shared_ptr<Privateimpl>(new Privateimpl);
            }


};
/////CLIENT CODE////
shared_ptr<Interface1> first(make(1));
shared_ptr<Interface1> second=make(0);
shared_ptr<Interface2> third=second->get();
shared_ptr<Interface2> fourth=first->get();
//does not matter how underlying implementation is implemented

Many class hierarchy designers familiar with Java, COM or other like it
will be very at home with this style. The point is that the class
implementor in free to use MI to implement interfaces, which has long
been a tried and true approach
"Intrusive pointers" have this same property, with the added benefit
that the shared_ptr does not have to be constructed from the complete
type, but the disadvantage that every class has to inherit from the
same object manager interface. I personally prefer intrusive style.

Gotcha!!! You asked
Calling shared_from_this() inside a constructor
struct Parent;
struct Child{
shared_ptr<Parent> p;
Child(shared_ptr<Parent> _p):p(_p){
if(/*something*/)throw 1;
}
};
struct Parent:enable_shared_from_this<Parent>{
weak_ptr<Child> c;
Parent(){
   shared_ptr<Child> d(new Child(shared_from_this());
   c=weak_ptr<Child>(d);

}
};

This does not work no matter how you try it. A ref counted shared
pointer, intrusive or non,needs to manage a completely constructed
object. Basically the above translates to
shared_ptr<Parent> _tmp(this);
and if Child happened to throw, _tmp tries to delete an object that was
never constructed. That is bad, and there is no workaround. The child
objects have to get info about the Parent after Parent has been fully
constructed


Others
1.Deriving from a class that derives from enable_shared_from_this. I'm
sure the docs say somewhere that <T> must be the complete --i.e most
derived--  type. If they don't they need to.
2. Sometimes legacy shared_ptr that do not support weak_ptr semantics
are facelifted to look like tr1::shared_ptr as much as they can.
Intrusive style pointers can be made to have the same semantics as
enable_shared_from_this, however non-intrusive will require far more
machinery.
Hope this helps

Javier wrote:
> I'm trying to find the rationale behind this class beyond the Boost
> documentation
> (http://www.boost.org/libs/smart_ptr/sp_techniques.html#from_this) and
> Pete Becker's C++ Standard Library Extensions book (section 2.6, pp
> 57).
>
> I understand the technicalities of the class, but what are some
> practical uses?  Are there any "gotchas"?  It would seem to me that
> once a class is "smartified" there are implications in a design
> regarding its lifetime.
>
> Regards,
>
> Javier
>
> ---
> [ comp.std.c++ is moderated.  To submit articles, try just posting with ]
> [ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
> [              --- Please see the FAQ before posting. ---               ]
> [ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]