
To Peter Dimov Hi, Peter Could you, please, tell if is there a smart pointer template to a class data-member. I failed to find an appropriate pointer among five ones existing in the boost. I need it for the two tasks appended. By this time, I have written an extension to intrusive_ptr to meet these needs. If you or anyone else are interested I could post it or send in person. Oleg boostoleg@yandex.ru Task 1) class A { public: A(){ m_wData = 0;} WORD* GetData(){ return &m_wData} A* CreateA( int nOffset){ return new A;} protected: WORD m_wData; }; main() { A aInstance; WORD* pData = aInstance.CreateA( 15)->GetData(); *pData = 57; // access violation } Task 2) class DocActiveX { protected: Tree* m_pTree; public: ~DocActiveX(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} }; class DocDirect { protected: Tree* m_pTree; public: ~DocDirect(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} }; class ShowExternal { public: bool Show( const Tree* pTree); }; class SomeClass { protected: Tree* m_pTree; }; void SomeClass::OnLoad() { DocActiveX* pInstance = new DocActiveX; aInstance.Load( lpszSomePath); m_pTree = aInstance.Parse(); } void SomeClass::OnShow() { ShowExternal ashow; ashow.Show( m_pTree); }

Oleg Fedtchenko wrote:
To Peter Dimov
Hi, Peter
Could you, please, tell if is there a smart pointer template to a class data-member. I failed to find an appropriate pointer among five ones existing in the boost.
I need it for the two tasks appended.
No, none of the existing pointers address the problem directly, although shared_ptr can be persuaded to do what you want.
Task 2)
class DocActiveX { protected: Tree* m_pTree;
public: ~DocActiveX(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} };
That's the easy part. class DocActiveX { private: shared_ptr<Tree> m_pTree; public: bool Load( const char* lpszPath ); shared_ptr<Tree> Parse() { return m_pTree; } };
Task 1)
class A { public: A(){ m_wData = 0;} WORD* GetData(){ return &m_wData} A* CreateA( int nOffset){ return new A;}
protected: WORD m_wData; };
This is a bit harder. class A { private: WORD m_wData; private: A(); public: static shared_ptr<A> CreateA() { return shared_ptr<A>(new A); } friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) { return shared_ptr<WORD>( &this_->m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } }; int main() { shared_ptr<A> pa = A::CreateA(); shared_ptr<WORD> pw = GetData(pa); pa.reset(); std::cout << "*pw: " << *pw << '\n'; }

Peter Dimov wrote:
This is a bit harder.
class A { private:
WORD m_wData;
private:
A();
public:
static shared_ptr<A> CreateA() { return shared_ptr<A>(new A); }
friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) { return shared_ptr<WORD>( &this_->m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } };
int main() { shared_ptr<A> pa = A::CreateA(); shared_ptr<WORD> pw = GetData(pa); pa.reset(); std::cout << "*pw: " << *pw << '\n'; }
I'm implementing a mixture of smart_ptr and boost.langbinding libraries. It's in early stage and I don't know yet what it's best for. Candidate number one is a transaction framework. Pointers to objects are smart, of course. They always point to full object and they store dynamic type of the object. Plus, smart pointers hold a pointer to static type. You can clone and swap pointee object. And you can extact a member from it. object_ptr<Y> py(new Y); object_ptr<X> px = py.get_member(&Y::x); // Works even though static types are unknown: object_ptr<void> pvy(py); object_ptr<void> pvx py.get_member(&Y::x); // Other operations: object_ptr<void> copy = pvx.clone(); copy.swap_object(pvx); -- Alexander Nasonov

Oleg Fedtchenko wrote:
class DocActiveX { protected: Tree* m_pTree;
public: ~DocActiveX(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} };
Peter Dimov wrote:
That's the easy part.
class DocActiveX { private:
shared_ptr<Tree> m_pTree;
public:
bool Load( const char* lpszPath ); shared_ptr<Tree> Parse() { return m_pTree; } };
Ok. But if m_pTree has a pointer to its owner (of type DocActiveX) then it's possible that m_pTree will access through that pointer after its owner is deleted. Using shared_ptr to its owner will cause a deadlock (a member cannot have a shared_ptr to its owner). Another task is: class DocActiveX { private: Tree m_Tree; // it's an object, not a pointer public: bool Load( const char* lpszPath ); Tree* Parse() { return &m_Tree; } }; To allow creating a safe shared_ptr pointing to a class member there is need in such a code: shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; } And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p): template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...} It's good as it allows to use shared_ptr. But it's good if this class will never be used as an object (nondynamic) data-member inside another owner. Also it's good when we create shared_ptr using ptrOwner outside a class because we can pass in a shared_ptr of any owner. But when we use shared_ptr inside a class (like in this example) it has three drawbacks. The body of the function must be aware of the type of an owner. It prevents this code from being used in a DLL until the owner type is well-known (e.g. declared in boost). Another thing is that it's impossible to use custom add_ref() and release() for an owner the way it is done in intrusive_ptr. I mean a developer of a class must choose one of the shared_ptr and intrusive_ptr (if the last is extended to support a smart member pointers). But we want this class to be used inside owner of any type. And one owner will live with just *delete me* but another owner will require *let me do some processing and then delete me myself*. And the third shortcoming is that it is clogging the code with the second parameter (for an owner shared_ptr) when there are many functions returning member pointers. It could be avoided by storing a pointer to an owner somewhere inside a class. All of these drawbacks could be eliminated by using an owner pointer stored in a base class for every of the members and the owner itself. The base class would be of some predefined type. There could be a hierarchy of two base classes. One of them (member_base) stores a pointer to an owner (for both members and an owner). And the second (top) (owner_base) that stores a ref count and has overidable add_ref() and release() (for an owner only). Using overidable add_ref() and release() makes it possible to derive a class with these functions customized (like in intrusive_ptr). Calling add_ref() and release() directly through an owner pointer allows to avoid writing additional intrusive_ptr_add_ref and intrusive_ptr_release functions for every owner type until you need some extra processing. Also you could create a set of owner_base derivative classes using classical polymorphism. The idea of using a base class for intrusive_ptr was already spoken by Stefan Slapeta at http://lists.boost.org/MailArchives/boost/msg06973.php Intrusive_ptr cannot be used directly as is for a member_ptr because a member_ptr must keep two different pointers: to a member (exposed by get()) and to a deletable owner for which add_ref() and release() are called. It must be a template of two arguments (the second is for a pointer to an owner). Shared_ptr should not be used for this task in a whole as it doesn't allow to call custom add_ref(). And one more thing, to create a shared_ptr for a member we must pass to its ctor a smart pointer that keeps the use_count of an owner and is stored inside a class creating the smart pointer. Weak_ptr could be used for that as it doesn't cause a deadlock. But when we create shared_ptr based on an expired weak_ptr an exception is thrown. And weak_ptr is always expiredwhen an owner pointed to by this weak_ptr is created as an object data-member inside another upper-level owner, not by using op *new*. Could you or anyone else say if this above and below is worth using? Oleg Fedtchenko BoostOleg@yandex.ru * * * * * * * * * * * * * * * * * * * * * * * * * This is possible usage of member_ptr: #include "member_ptr.hpp" class A : public member_ptr_owner_base { public: A( member_ptr_owner_base* pOwner =0xFFFFFFFF) : member_ptr_owner_base( pOwner) { m_wData = 0;} member_ptr<WORD> GetData() { return member_ptr<WORD>(&m_wData, get_owner()); // increments ref_count_ of the owner if it's not 0 } protected: WORD m_wData; }; class B : public member_ptr_owner_base { public: B( member_ptr_owner_base* pOwner =0xFFFFFFFF) // see ctor // member_ptr_owner_base::this_type( this_type* pw) // for meaning of 0xFFFFFFFF // // 0 should be passed when B is created as // an object (nondynamic) data-member; // // you can pass in 0 for any owner // or your owner must be member_ptr_owner_base // derivative to handle references on it and for // the class B could be placed inside a DLL // (when its functions are not inlined) : member_ptr_owner_base( pOwner), m_a(get_owner()) {} shared_ptr<A> CreateA( int nOffset) { return shared_ptr<A>(new A); } member_ptr<A> CreateSafeA( int nOffset) { return member_ptr<A>(new A); // returned member_ptr has ref_count_ 1 // // newly created A is an owner of itself, // ( see ctor member_ptr(T * px) ) // it will be deleted when released } member_ptr<A> GetA() { return member_ptr<A>(&m_a, get_owner()); // an owner will be deleted, not m_a } // // but owner will be deleted only when // all member_ptrs to m_a are released // // if owner is 0 then nothing will be deleted // but &m_a will always be exposed // (member_ptr.get()...) member_ptr<B> GetPtr() { return member_ptr<B>(this); } protected: A m_a; }; main() { intrusive_ptr<B> pB = intrusive_ptr<B>(new B); // member_ptr_owner_base has already got // intrusive_ptr_add_ref() and // intrusive_ptr_release() for itself WORD* pDataExcept = pB->CreateA( 15)->GetData(); *pDataExcept = 57; // access violation member_ptr<WORD> pData = pB->CreateSafeA( 15)->GetData(); *pData = 57; // safe access // ref_count_ changing member_ptr<B> ptrB = pB->GetPtr(); // increments ref_count_ for pB from 1 to 2 member_ptr<A> ptrA = ptrB->GetA(); // increments ref_count_ for pB from 2 to 3 *ptrA->GetData() = 91; std::cout << "data: " << *pB->GetA()->GetData() << '\n'; // will print *91* } * * * * * * * * * * * * * * * * * * * * * * * * * This is another possible usage of member_ptr: (Pointer to an owner is released, but a pointer to its member is further used across a program) class DocActiveX : public member_ptr_owner_base { protected: Tree* m_pTree; public: DocActiveX(member_ptr_owner_base* pOwner) : member_ptr_owner_base(pOwner){} ~DocActiveX() { if(m_pTree) delete m_pTree; } bool Load( const char* lpszPath); member_ptr<Tree> Parse() { return member_ptr<Tree>(m_pTree, get_owner()); } }; class SomeClass { public: SomeClass() : m_pTree(this){} protected: member_ptr<Tree> m_pTree; }; void SomeClass::OnLoad() { DocActiveX* pInstance = new DocActiveX; // pInstance is an owner of itself // (see ctor // member_ptr_owner_base::this_type( this_type* pw) // ) pInstance->Load( lpszSomePath); m_pTree = pInstance->Parse(); // m_pTree points to DocActiveX::m_pTree and has pInstance as its owner // // pInstance will be automatically deleted by SomeClass::m_pTree // when SomeClass::m_pTree is released (when SomeClass is destructed // if ashow.Show( m_pTree) did not pass m_pTree into another thread) } void SomeClass::OnShow() { ShowExternal ashow; ashow.Show( m_pTree); } * * * * * * * * * * * * * * * * * * * * * * * * * This is the member_ptr Code for this smart pointer I created as an extension to intrusive_ptr (copyrighted by Peter Dimov) to hold two different pointers: to a member (exposed by get()) and to a deletable owner for which add_ref() and release() are called. #ifndef BOOST_MEMBER_PTR_HPP_INCLUDED #define BOOST_MEMBER_PTR_HPP_INCLUDED // // member_ptr.hpp // // This file is a derivative work from intrusive_ptr.hpp by Peter Dimov // // Copyright (c) 2004 Oleg Fedtchenko // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // #include <boost/config.hpp> #ifdef BOOST_MSVC // moved here to work around VC++ compiler crash # pragma warning(push) # pragma warning(disable:4284) // odd return type for operator-> #endif #include <boost/assert.hpp> #include <boost/detail/workaround.hpp> #include <functional> // for std::less #include <iosfwd> // for std::basic_ostream namespace boost { // // base classes used to work with member_ptr // // // // base class for all members // class member_ptr_owner_base; class member_ptr_member_base { typedef member_ptr_member_base this_type; typedef member_ptr_owner_base owner_type; owner_type* pw_; public: this_type( owner_type* pw) : pw_(pw){} virtual ~member_ptr_member_base(){} virtual owner_type* get_owner() const{ return pw_;} }; // // base class for all owners // class member_ptr_owner_base : public member_ptr_member_base { typedef member_ptr_member_base base_type; typedef member_ptr_owner_base this_type; protected: int ref_count_; public: this_type( this_type* pw) : base_type( (pw == (this_type*)0xFFFFFFFF) ? this : pw), ref_count_(0){} virtual ~member_ptr_owner_base(){} virtual void add_ref() { ++ref_count_; } virtual void release() { if (--ref_count_ == 0) { delete this; } } virtual int get_ref_count() const { return ref_count_; } }; // // these functions are called by // intrusive_ptr<member_ptr_owner_base> // inline void intrusive_ptr_add_ref(member_ptr_owner_base * p) { p->add_ref(); } inline void intrusive_ptr_release(member_ptr_owner_base * p) { p->release(); } // // member_ptr // // A smart pointer that uses intrusive-like reference counting for an owner of a class data-member. // // The object is responsible for destroying itself // (if member_ptr_owner_base is not its direct ancestor). // template<class T> class member_ptr { private: typedef member_ptr this_type; public: typedef T member_type; typedef member_ptr_owner_base owner_type; member_ptr(): px_(0), pw_(0) { } member_ptr(T * px, owner_type * pw, bool to_add_ref = true): px_(px), pw_(pw) { if( to_add_ref) add_ref(); } member_ptr(T * px): px_(px), pw_(px->get_owner()) { add_ref(); } #if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) template<class U> member_ptr(member_ptr<U> const & rhs): px_(rhs.get()), pw_(rhs.get_owner()) { add_ref(); } #endif member_ptr(member_ptr const & rhs): px_(rhs.px_), pw_(rhs.pw_) { add_ref(); } ~member_ptr() { release_ref(); } void add_ref() { // to avoid exception when using member_ptr inside an owner ctor if(pw_ != 0 && ( ((void*)px_ == (void*)pw_) || pw_->get_ref_count() > 0)) { pw_->add_ref(); } } void release_ref() { if(pw_ != 0 && pw_->get_ref_count() > 0) { pw_->release(); } } #if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) template<class U> member_ptr & operator=(member_ptr<U> const & rhs) { this_type(rhs).swap(*this); return *this; } #endif member_ptr & operator=(member_ptr const & rhs) { this_type(rhs).swap(*this); return *this; } /* unsafe - there is no owner specified member_ptr & operator=(T * rhs) { this_type(rhs).swap(*this); return *this; } */ T * get() const { return px_; } owner_type * get_owner() const { return pw_; } T & operator*() const { return *px_; } T * operator->() const { return px_; } #if defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x530) operator bool () const { return px_ != 0; } #elif defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) typedef T * (this_type::*unspecified_bool_type)() const; operator unspecified_bool_type() const // never throws { return px_ == 0? 0: &this_type::get; } #else typedef T * this_type::*unspecified_bool_type; operator unspecified_bool_type () const { return px_ == 0? 0: &this_type::px_; } #endif // operator! is a Borland-specific workaround bool operator! () const { return px_ == 0; } void swap(member_ptr & rhs) { T * tmpX = px_; owner_type * tmpW = pw_; px_ = rhs.px_; pw_ = rhs.pw_; rhs.px_ = tmpX; rhs.pw_ = tmpW; } private: T * px_; owner_type * pw_; }; template<class T, class U> inline bool operator==(member_ptr<T> const & a, member_ptr<U> const & b) { return a.get() == b.get(); } template<class T, class U> inline bool operator!=(member_ptr<T> const & a, member_ptr<U> const & b) { return a.get() != b.get(); } template<class T> inline bool operator==(member_ptr<T> const & a, T * b) { return a.get() == b; } template<class T> inline bool operator!=(member_ptr<T> const & a, T * b) { return a.get() != b; } template<class T> inline bool operator==(T * a, member_ptr<T> const & b) { return a == b.get(); } template<class T> inline bool operator!=(T * a, member_ptr<T> const & b) { return a != b.get(); } #if __GNUC__ == 2 && __GNUC_MINOR__ <= 96 // Resolve the ambiguity between our op!= and the one in rel_ops template<class T> inline bool operator!=(member_ptr<T> const & a, member_ptr<T> const & b) { return a.get() != b.get(); } #endif template<class T> inline bool operator<(member_ptr<T> const & a, member_ptr<T> const & b) { return std::less<T *>()(a.get(), b.get()); } template<class T> void swap(member_ptr<T> & lhs, member_ptr<T> & rhs) { lhs.swap(rhs); } // mem_fn support template<class T> T * get_pointer(member_ptr<T> const & p) { return p.get(); } template<class T> member_ptr<T>::owner_type * get_owner(member_ptr<T> const & p) { return p.get_owner(); } template<class T, class U> member_ptr<T> static_pointer_cast(member_ptr<U> const & p) { return member_ptr<T,W>( static_cast<T *>(p.get()), static_cast<member_ptr<T>::owner_type *>(p.get_owner())); } template<class T, class U> member_ptr<T> const_pointer_cast(member_ptr<U> const & p) { return member_ptr<T,W>( const_cast<T *>(p.get()), const_cast<member_ptr<T>::owner_type *>(p.get_owner())); } template<class T, class U> member_ptr<T> dynamic_pointer_cast(member_ptr<U> const & p) { return member_ptr<T>( dynamic_cast<T *>(p.get()), dynamic_cast<member_ptr<T>::owner_type *>(p.get_owner())); } // operator<< #if defined(__GNUC__) && (__GNUC__ < 3) template<class Y> std::ostream & operator<< (std::ostream & os, member_ptr<Y> const & p) { os << p.get(); return os; } #else # if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, <= 1200 && __SGI_STL_PORT) // MSVC6 has problems finding std::basic_ostream through the using declaration in namespace _STL using std::basic_ostream; template<class E, class T, class Y> basic_ostream<E, T> & operator<< (basic_ostream<E, T> & os, member_ptr<Y> const & p) # else template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, member_ptr<Y> const & p) # endif { os << p.get(); return os; } #endif } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) #endif #endif // #ifndef BOOST_MEMBER_PTR_HPP_INCLUDED // end of member_ptr

For boost list moderators: Please, replace the previous my email by this one as this email has the same content but the better formatting to view it in a Web-based mailbox.
Oleg Fedtchenko wrote:
class DocActiveX { protected: Tree* m_pTree;
public: ~DocActiveX(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} };
Peter Dimov wrote:
That's the easy part.
class DocActiveX { private:
shared_ptr<Tree> m_pTree;
public:
bool Load( const char* lpszPath ); shared_ptr<Tree> Parse() { return m_pTree; } };
Ok. But if m_pTree has a pointer to its owner (of type DocActiveX) then it's possible that m_pTree will access through that pointer after its owner is deleted. Using shared_ptr to its owner will cause a deadlock (a member cannot have a shared_ptr to its owner). Another task is: class DocActiveX { private: Tree m_Tree; // it's an object, not a pointer public: bool Load( const char* lpszPath ); Tree* Parse() { return &m_Tree; } }; To allow creating a safe shared_ptr pointing to a class member there is need in such a code: shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; } And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p): template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...} It's good as it allows to use shared_ptr. But it's good if this class will never be used as an object (nondynamic) data-member inside another owner. Also it's good when we create shared_ptr using ptrOwner outside a class because we can pass in a shared_ptr of any owner. But when we use shared_ptr inside a class (like in this example) it has three drawbacks. The body of the function must be aware of the type of an owner. It prevents this code from being used in a DLL until the owner type is well-known (e.g. declared in boost). Another thing is that it's impossible to use custom add_ref() and release() for an owner the way it is done in intrusive_ptr. I mean a developer of a class must choose one of the shared_ptr and intrusive_ptr (if the last is extended to support a smart member pointers). But we want this class to be used inside owner of any type. And one owner will live with just *delete me* but another owner will require *let me do some processing and then delete me myself*. And the third shortcoming is that it is clogging the code with the second parameter (for an owner shared_ptr) when there are many functions returning member pointers. It could be avoided by storing a pointer to an owner somewhere inside a class. All of these drawbacks could be eliminated by using an owner pointer stored in a base class for every of the members and the owner itself. The base class would be of some predefined type. There could be a hierarchy of two base classes. One of them (member_base) stores a pointer to an owner (for both members and an owner). And the second (top) (owner_base) that stores a ref count and has overidable add_ref() and release() (for an owner only). Using overidable add_ref() and release() makes it possible to derive a class with these functions customized (like in intrusive_ptr). Calling add_ref() and release() directly through an owner pointer allows to avoid writing additional intrusive_ptr_add_ref and intrusive_ptr_release functions for every owner type until you need some extra processing. Also you could create a set of owner_base derivative classes using classical polymorphism. The idea of using a base class for intrusive_ptr was already spoken by Stefan Slapeta at http://lists.boost.org/MailArchives/boost/msg06973.php Intrusive_ptr cannot be used directly as is for a member_ptr because a member_ptr must keep two different pointers: to a member (exposed by get()) and to a deletable owner for which add_ref() and release() are called. It must be a template of two arguments (the second is for a pointer to an owner). Shared_ptr should not be used for this task in a whole as it doesn't allow to call custom add_ref(). And one more thing, to create a shared_ptr for a member we must pass to its ctor a smart pointer that keeps the use_count of an owner and is stored inside a class creating the smart pointer. Weak_ptr could be used for that as it doesn't cause a deadlock. But when we create shared_ptr based on an expired weak_ptr an exception is thrown. And weak_ptr is always expiredwhen an owner pointed to by this weak_ptr is created as an object data-member inside another upper-level owner, not by using op *new*. Could you or anyone else say if this above and below is worth using? Oleg Fedtchenko BoostOleg@yandex.ru * * * * * * * * * * * * * * * * * * * * * * * * * This is possible usage of member_ptr: #include "member_ptr.hpp" To allow creating a safe shared_ptr pointing to a class member there is class A : public member_ptr_owner_base { public: A( member_ptr_owner_base* pOwner =0xFFFFFFFF) : member_ptr_owner_base( pOwner) { m_wData = 0;} member_ptr<WORD> GetData() { // increments ref_count_ of the owner if it's not 0 return member_ptr<WORD>(&m_wData, get_owner()); } protected: WORD m_wData; }; class B : public member_ptr_owner_base { public: // see ctor // member_ptr_owner_base::this_type( this_type* pw) // for meaning of 0xFFFFFFFF // // 0 should be passed when B is created as // an object (nondynamic) data-member; // // you can pass in 0 for any owner // or your owner must be member_ptr_owner_base derivative to handle // references on it and for the class B could be placed inside a DLL // (when its functions are not inlined) B( member_ptr_owner_base* pOwner =0xFFFFFFFF) : member_ptr_owner_base( pOwner), m_a(get_owner()) {} shared_ptr<A> CreateA( int nOffset) { return shared_ptr<A>(new A); } member_ptr<A> CreateSafeA( int nOffset) { return member_ptr<A>(new A); // returned member_ptr has ref_count_ 1 // // newly created A is an owner of itself, // ( see ctor member_ptr(T * px) ) // it will be deleted when released } member_ptr<A> GetA() { return member_ptr<A>(&m_a, get_owner()); // an owner will be deleted, not m_a // // but owner will be deleted only when // all member_ptrs to m_a are released // // if owner is 0 then nothing will be deleted // but &m_a will always be exposed // (member_ptr.get()...) } member_ptr<B> GetPtr() { return member_ptr<B>(this); } protected: A m_a; }; main() { // member_ptr_owner_base has already got // intrusive_ptr_add_ref() and // intrusive_ptr_release() for itself intrusive_ptr<B> pB = intrusive_ptr<B>(new B); WORD* pDataExcept = pB->CreateA( 15)->GetData(); *pDataExcept = 57; // access violation member_ptr<WORD> pData = pB->CreateSafeA( 15)->GetData(); *pData = 57; // safe access // ref_count_ changing // the next increments ref_count_ for pB from 1 to 2 member_ptr<B> ptrB = pB->GetPtr(); // the next increments ref_count_ for pB from 2 to 3 member_ptr<A> ptrA = ptrB->GetA(); *ptrA->GetData() = 91; // will print *91* std::cout << "data: " << *pB->GetA()->GetData() << '\n'; } * * * * * * * * * * * * * * * * * * * * * * * * * This is another possible usage of member_ptr: (Pointer to an owner is released, but a pointer to its member is further used across a program) class DocActiveX : public member_ptr_owner_base { protected: Tree* m_pTree; public: DocActiveX(member_ptr_owner_base* pOwner) : member_ptr_owner_base(pOwner){} ~DocActiveX() { if(m_pTree) delete m_pTree; } bool Load( const char* lpszPath); member_ptr<Tree> Parse() { return member_ptr<Tree>(m_pTree, get_owner()); } }; class SomeClass { public: SomeClass() : m_pTree(this){} protected: member_ptr<Tree> m_pTree; }; void SomeClass::OnLoad() { // pInstance in the next is an owner of itself // (see ctor member_ptr_owner_base::this_type( this_type* pw) ) DocActiveX* pInstance = new DocActiveX; pInstance->Load( lpszSomePath); m_pTree = pInstance->Parse(); // m_pTree points to DocActiveX::m_pTree and has pInstance as its owner // // pInstance will be automatically deleted by SomeClass::m_pTree // when SomeClass::m_pTree is released (when SomeClass is destructed // if ashow.Show( m_pTree) did not pass m_pTree into another thread) } void SomeClass::OnShow() { ShowExternal ashow; ashow.Show( m_pTree); } * * * * * * * * * * * * * * * * * * * * * * * * * This is the member_ptr Code for this smart pointer I created as an extension to intrusive_ptr (copyrighted by Peter Dimov) to hold two different pointers: to a member (exposed by get()) and to a deletable owner for which add_ref() and release() are called. #ifndef BOOST_MEMBER_PTR_HPP_INCLUDED #define BOOST_MEMBER_PTR_HPP_INCLUDED // // member_ptr.hpp // // This file is a derivative work from intrusive_ptr.hpp by Peter Dimov // // Copyright (c) 2004 Oleg Fedtchenko // // Permission to copy, use, modify, sell and distribute this software // is granted provided this copyright notice appears in all copies. // This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // #include <boost/config.hpp> #ifdef BOOST_MSVC // moved here to work around VC++ compiler crash # pragma warning(push) # pragma warning(disable:4284) // odd return type for operator-> #endif #include <boost/assert.hpp> #include <boost/detail/workaround.hpp> #include <functional> // for std::less #include <iosfwd> // for std::basic_ostream namespace boost { // // base classes used to work with member_ptr // // // // base class for all members // class member_ptr_owner_base; class member_ptr_member_base { typedef member_ptr_member_base this_type; typedef member_ptr_owner_base owner_type; owner_type* pw_; public: this_type( owner_type* pw) : pw_(pw){} virtual ~member_ptr_member_base(){} virtual owner_type* get_owner() const{ return pw_;} }; // // base class for all owners // class member_ptr_owner_base : public member_ptr_member_base { typedef member_ptr_member_base base_type; typedef member_ptr_owner_base this_type; protected: int ref_count_; public: this_type( this_type* pw) :base_type( (pw == (this_type*)0xFFFFFFFF) ? this : pw), ref_count_(0) {} virtual ~member_ptr_owner_base(){} virtual void add_ref() { ++ref_count_; } virtual void release() { if (--ref_count_ == 0) { delete this; } } virtual int get_ref_count() const { return ref_count_; } }; // // these functions are called by // intrusive_ptr<member_ptr_owner_base> // inline void intrusive_ptr_add_ref(member_ptr_owner_base * p) { p->add_ref(); } inline void intrusive_ptr_release(member_ptr_owner_base * p) { p->release(); } // // member_ptr // // A smart pointer that uses intrusive-like reference counting for // an owner of a class data-member. // // The object is responsible for destroying itself // (if member_ptr_owner_base is not its direct ancestor). // template<class T> class member_ptr { private: typedef member_ptr this_type; public: typedef T member_type; typedef member_ptr_owner_base owner_type; member_ptr(): px_(0), pw_(0) { } member_ptr(T * px, owner_type * pw, bool to_add_ref = true) : px_(px), pw_(pw) { if( to_add_ref) add_ref(); } member_ptr(T * px): px_(px), pw_(px->get_owner()) { add_ref(); } #if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) template<class U> member_ptr(member_ptr<U> const & rhs) : px_(rhs.get()), pw_(rhs.get_owner()) { add_ref(); } #endif member_ptr(member_ptr const & rhs): px_(rhs.px_), pw_(rhs.pw_) { add_ref(); } ~member_ptr() { release_ref(); } void add_ref() { if( pw_ != 0 && // to avoid exception when using member_ptr inside // an owner ctor ( ((void*)px_ == (void*)pw_) || pw_->get_ref_count()>0)) { pw_->add_ref(); } } void release_ref() { if(pw_ != 0 && pw_->get_ref_count() > 0) { pw_->release(); } } #if !defined(BOOST_NO_MEMBER_TEMPLATES) || defined(BOOST_MSVC6_MEMBER_TEMPLATES) template<class U> member_ptr & operator=(member_ptr<U> const & rhs) { this_type(rhs).swap(*this); return *this; } #endif member_ptr & operator=(member_ptr const & rhs) { this_type(rhs).swap(*this); return *this; } /* unsafe - there is no owner specified member_ptr & operator=(T * rhs) { this_type(rhs).swap(*this); return *this; } */ T * get() const { return px_; } owner_type * get_owner() const { return pw_; } T & operator*() const { return *px_; } T * operator->() const { return px_; } #if defined(__SUNPRO_CC) && BOOST_WORKAROUND(__SUNPRO_CC, <= 0x530) operator bool () const { return px_ != 0; } #elif defined(__MWERKS__) && BOOST_WORKAROUND(__MWERKS__, BOOST_TESTED_AT(0x3003)) typedef T * (this_type::*unspecified_bool_type)() const; operator unspecified_bool_type() const // never throws { return px_ == 0? 0: &this_type::get; } #else typedef T * this_type::*unspecified_bool_type; operator unspecified_bool_type () const { return px_ == 0? 0: &this_type::px_; } #endif // operator! is a Borland-specific workaround bool operator! () const { return px_ == 0; } void swap(member_ptr & rhs) { T * tmpX = px_; owner_type * tmpW = pw_; px_ = rhs.px_; pw_ = rhs.pw_; rhs.px_ = tmpX; rhs.pw_ = tmpW; } private: T * px_; owner_type * pw_; }; template<class T, class U> inline bool operator==(member_ptr<T> const & a, member_ptr<U> const & b) { return a.get() == b.get(); } template<class T, class U> inline bool operator!=(member_ptr<T> const & a, member_ptr<U> const & b) { return a.get() != b.get(); } template<class T> inline bool operator==(member_ptr<T> const & a, T * b) { return a.get() == b; } template<class T> inline bool operator!=(member_ptr<T> const & a, T * b) { return a.get() != b; } template<class T> inline bool operator==(T * a, member_ptr<T> const & b) { return a == b.get(); } template<class T> inline bool operator!=(T * a, member_ptr<T> const & b) { return a != b.get(); } #if __GNUC__ == 2 && __GNUC_MINOR__ <= 96 // Resolve the ambiguity between our op!= and the one in rel_ops template<class T> inline bool operator!=(member_ptr<T> const & a, member_ptr<T> const & b) { return a.get() != b.get(); } #endif template<class T> inline bool operator<(member_ptr<T> const & a, member_ptr<T> const & b) { return std::less<T *>()(a.get(), b.get()); } template<class T> void swap(member_ptr<T> & lhs, member_ptr<T> & rhs) { lhs.swap(rhs); } // mem_fn support template<class T> T * get_pointer(member_ptr<T> const & p) { return p.get(); } template<class T> member_ptr<T>::owner_type * get_owner(member_ptr<T> const & p) { return p.get_owner(); } template<class T, class U> member_ptr<T> static_pointer_cast(member_ptr<U> const & p) { return member_ptr<T,W>( static_cast<T *>(p.get()), static_cast<member_ptr<T>::owner_type *> (p.get_owner())); } template<class T, class U> member_ptr<T> const_pointer_cast(member_ptr<U> const & p) { return member_ptr<T,W>( const_cast<T *>(p.get()), const_cast<member_ptr<T>::owner_type *> (p.get_owner())); } template<class T, class U> member_ptr<T> dynamic_pointer_cast(member_ptr<U> const & p) { return member_ptr<T>( dynamic_cast<T *>(p.get()), dynamic_cast<member_ptr<T>::owner_type *> (p.get_owner())); } // operator<< #if defined(__GNUC__) && (__GNUC__ < 3) template<class Y> std::ostream & operator<< (std::ostream & os, member_ptr<Y> const & p) { os << p.get(); return os; } #else # if defined(BOOST_MSVC) && BOOST_WORKAROUND(BOOST_MSVC, <= 1200 && __SGI_STL_PORT) // MSVC6 has problems finding std::basic_ostream through the using declaration in namespace _STL using std::basic_ostream; template<class E, class T, class Y> basic_ostream<E, T> & operator<< (basic_ostream<E, T> & os, member_ptr<Y> const & p) # else template<class E, class T, class Y> std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os, member_ptr<Y> const & p) # endif { os << p.get(); return os; } #endif } // namespace boost #ifdef BOOST_MSVC # pragma warning(pop) #endif #endif // #ifndef BOOST_MEMBER_PTR_HPP_INCLUDED // end of member_ptr

"Oleg Fedtchenko" <BoostOleg@yandex.ru> writes:
For boost list moderators: Please, replace the previous my email by this one as this email has the same content but the better formatting to view it in a Web-based mailbox.
We don't have the power to replace emails. Once your first post is approved, we stop moderating you. But while I have your attention, it would be very polite if you could introduce line breaks in your messages every 65 columns or so. Thanks, Dave -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

On 10/12/2004 09:52 AM, Oleg Fedtchenko wrote: [snip]
Oleg Fedtchenko wrote:
class DocActiveX { protected: Tree* m_pTree;
public: ~DocActiveX(){ if(m_pTree) delete m_pTree;} bool Load( const char* lpszPath); Tree* Parse(){ return m_pTree;} };
Peter Dimov wrote:
That's the easy part.
class DocActiveX { private:
shared_ptr<Tree> m_pTree;
public:
bool Load( const char* lpszPath ); shared_ptr<Tree> Parse() { return m_pTree; } };
Ok. But if m_pTree has a pointer to its owner (of type DocActiveX) then it's possible that m_pTree will access through that pointer after its owner is deleted. Using shared_ptr to its owner will cause a deadlock (a member cannot have a shared_ptr to its owner).
A solution to this problem of cyclic references is in the sandbox under managed_ptr.
Another task is:
class DocActiveX { private:
Tree m_Tree; // it's an object, not a pointer
public:
bool Load( const char* lpszPath ); Tree* Parse() { return &m_Tree; } };
To allow creating a safe shared_ptr pointing to a class member there is need in such a code: ^^^^^^^^^^^^^^^^^^^^^^ The following is clearer to me:
To allow creating a safe shared_ptr pointing to a class member there is the need for a new shared_ptr contructor that can be used as follows:
shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; }
And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p):
template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...}
It's good as it allows to use shared_ptr.
I think this amounts to enabling "interior" pointers to be used as arguments to shared_ptr CTOR. An "interior" pointer is a pointer to the interior, not the beginning, to an object. I believe the original reason for share_ptr<Z>::px was to allow its use for multiply inherited objects and allow conversion to the 2nd superclass: class A{int a;};class B{float b;}; class AB: public A,B{}; shared_ptr<AB> pAB(new AB); shared_ptr<B> pB(pAB); However, now that you've mentioned it, I don't see any reason why it couldn't be used as proposed for a shared_ptr to B::b: share_ptr<float> pBb(&pB->b,pB);
But it's good if this class will never be used as an object (nondynamic) data-member inside another owner.
This is unclear to me. Could you provide an example?
Also it's good when we create shared_ptr using ptrOwner outside a class because we can pass in a shared_ptr of any owner.
Could you also provide an example for this?
But when we use shared_ptr inside a class (like in this example) it has three drawbacks.
The body of the function must be aware of the type of an owner. It prevents this code from being used in a DLL until the owner type is well-known (e.g. declared in boost).
Well, I think all the information needed is in shared_ptr::pn. After all, it knows the actual type of the owner. That was the reason for the two pointers, px and pn, in shared_ptr. The 2nd actually knows the type of the owner. Am I missing something?
Another thing is that it's impossible to use custom add_ref() and release() for an owner the way it is done in intrusive_ptr. I mean a developer of a class must choose one of the shared_ptr and intrusive_ptr (if the last is extended to support a smart member pointers). But we want this class to be used inside owner of any type. And one owner will live with just *delete me* but another owner will require *let me do some processing and then delete me myself*.
Can't *let me do some processing and then delete me myself* be done with a custom deleter argument to shared_ptr? I.e. with the CTOR: template<class Y, class D> shared_ptr(Y * p, D d): px(p), pn(p, d) { detail::sp_enable_shared_from_this( pn, p, p ); }

Oleg Fedtchenko wrote:
shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; }
And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p):
template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...}
The body of the function must be aware of the type of an owner. It prevents this code from being used in a DLL until the owner type is well-known (e.g. declared in boost).
Larry Evans wrote:
Well, I think all the information needed is in shared_ptr::pn. After all, it knows the actual type of the owner. That was the reason for the two pointers, px and pn, in shared_ptr. The 2nd actually knows the type of the owner. Am I missing something?
Ok. I meant the function Parse above cannot accept ptrOwner pointing to a type other than DocActiveX if its body is in a DLL. This could be resolved by using a templated form of that function. And its body could be placed in a DLL's header: template <class Z> shared_ptr<Tree> Parse( shared_ptr<Z>& ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; } But let's consider the next example: class A { protected: B b; C c; . . . K k; public: template <class Z> shared_ptr<B> GetB( shared_ptr<Z>& ptrOwner) { return shared_ptr<B>( &b, ptrOwner); } template <class Z> shared_ptr<C> GetC( shared_ptr<Z>& ptrOwner) { return shared_ptr<C>( &c, ptrOwner); } . . . template <class Z> shared_ptr<K> GetK( shared_ptr<Z>& ptrOwner) { return shared_ptr<K>( &k, ptrOwner); } }; Isn't it too clogging compared with the following: class A : public member_ptr_owner_base { protected: B b; C c; . . . K k; public: shared_ptr<B> GetB(); shared_ptr<C> GetC(); . . . shared_ptr<K> GetK(); }; Parameter ptrOwner is not used as a pointer to an owner is stored in a base class. And the body of all the functions can be nontemplated and kept in a cpp module (in a DLL too) without dublicating their object code. And one more thing. An owner of an object is specified only once (at object creating time) and used then many times (often automatically). But for templated function template <class Z> shared_ptr<B> GetB( shared_ptr<Z>& ptrOwner) { return shared_ptr<B>( &b, ptrOwner); } we must specify an owner every time and easily can pass in a shared_ptr other than pointing to the right owner: shared_ptr<A> pA(new A); shared_ptr<SOME> pSome(new SOME); shared_ptr<B> pB = pA->GetB( pSome); So who can say what is the better smart pointers to a class data-member: using this shared_ptr CTOR template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner, bool to_enable) : px(p), pn(ptrOwner.pn) { if(to_enable) { detail::sp_enable_shared_from_this(p, p, pn); } } // parameter to_enable is used do distinguish this CTOR from // existing CTOR // template<class Y, class D> shared_ptr(Y * p, D d): px(p), pn(p, d) // {...} or using member_ptr described at http://boostoleg.narod.ru/member_ptr.html Oleg Fedtchenko BoostOleg@yandex.ru http://boostoleg.narod.ru recommended thread entry http://lists.boost.org/MailArchives/boost/msg73168.php

On 10/14/2004 09:52 AM, Oleg Fedtchenko wrote: [snip]
we must specify an owner every time and easily can pass in a shared_ptr other than pointing to the right owner:
shared_ptr<A> pA(new A);
shared_ptr<SOME> pSome(new SOME);
shared_ptr<B> pB = pA->GetB( pSome);
Could you define a new shared_ptr templated CTOR which takes a pointer to member: template<typename Member> struct shared_ptr { template<typename Owner, Member Owner::*pm_type> shared_ptr(shared_ptr<Owner>* a_owner, pm_type a_pm) : px(a_owner.get()->a_pm), pn(???) {} }; I haven't compiled this; hence, the syntax may not be right. How would this compare with member_ptr, and the others?

Oleg Fedtchenko wrote:
shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; }
And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p):
template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...}
we must specify an owner every time and easily can pass in a shared_ptr other than pointing to the right owner:
Larry Evans wrote:
Could you define a new shared_ptr templated CTOR which takes a pointer to member:
template<typename Member> struct shared_ptr { template<typename Owner, Member Owner::*pm_type> shared_ptr(shared_ptr<Owner>* a_owner, pm_type a_pm) : px(a_owner.get()->a_pm), pn(???) {} };
I haven't compiled this; hence, the syntax may not be right. How would this compare with member_ptr, and the others?
I also did not compile this piece of code. But if I caught the idea behind it right, then to place constrains on acceptable combinations *member-owner* looks surprising. An owner knows all of it members exposed through shared_ptr. And it contains all of those acceptable combinations *type of owner and its corresponding acceptable type of member*. But I had a task of hierarchy of several classes two of which were used as owners for the same type of member exposed through a smart pointer. One of those owners had a pointer to another one. If I had used a CTOR with both pointers to a member and its owner (as at the beginning) I could have passed in the pointer to another (wrong) owner instead of *this*. And the type *member-owner* constrains above would have not prevented an access violation. But member_ptr keeping a pointer to an owner in its base class does not allow to make a mistake. Oleg Fedtchenko The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html thread entry http://lists.boost.org/MailArchives/boost/msg73168.php

On 10/18/2004 02:18 PM, Oleg Fedtchenko wrote: [snip]
I also did not compile this piece of code.
The syntax was wrong. It should be: template<class Y> shared_ptr(shared_ptr<Y> const & r, T Y::*m) : px(&(r.get()->*m)), pn(r.pn) { } which does compile.
But if I caught the idea behind it right, then to place constrains on acceptable combinations *member-owner* looks surprising.
An owner knows all of it members exposed through shared_ptr. And it contains all of those acceptable combinations *type of owner and its corresponding acceptable type of member*.
But I had a task of hierarchy of several classes two of which were used as owners for the same type of member exposed through a smart pointer. One of those owners had a pointer to another one. If I had used a CTOR with both pointers to a member and its owner (as at the beginning) I could have passed in the pointer to another (wrong) owner instead of *this*. And the type *member-owner* constrains above would have not prevented an access violation.
But member_ptr keeping a pointer to an owner in its base class does not allow to make a mistake. I'm having trouble understanding. Could you provide example code, or indicate whether the following is even close to your meaning:
struct member_type{}; enum owner_id{ id1, id2}; template<owner_id Id> struct other_id ; template<> struct other_id<id1> { static owner_id const the_id=id2; }; template<> struct other_id<id2> { static owner_id const the_id=id1; }; using namespace boost; template<owner_id Id> struct an_owner : public other_id<Id> { typedef other_id<Id> other_type; typedef an_owner<other_type::the_id> other_owner_type; member_type a_member; shared_ptr<member_type> p_member; an_owner(void){} an_owner(shared_ptr<other_owner_type>const & a_other) : p_member(a_other, &other_owner_type::a_member) {} }; int main() { { shared_ptr<an_owner<id1> > p1(new an_owner<id1>); shared_ptr<an_owner<id2> > p2(new an_owner<id2>(p1)); } return boost::report_errors(); }

Larry Evans wrote:
I'm having trouble understanding. Could you provide example code, or indicate whether the following is even close to your meaning:
Yes. It's not exactly what I meant but it's closer. I meant that an error could occur when passing a wrong owner in a function like this: template <class W> shared_ptr<T> Parse( shared_ptr<W> ptrOwner) { shared_ptr<T> ptrMember( &a_member, ptrOwner); return ptrMember; } because a_member must be a data-member of *ptrOwner itself or of any of its data-members. To reduce the possibility of this a class should contain a pointer to its owner inside that class and take it only once (through a CTOR or some set function). As you did in the next snippet.
Larry Evans wrote:
template<owner_id Id> struct an_owner : public other_id<Id> { typedef other_id<Id> other_type; typedef an_owner<other_type::the_id> other_owner_type; member_type a_member; shared_ptr<member_type> p_member; an_owner(void){} an_owner(shared_ptr<other_owner_type>const & a_other) : p_member(a_other, &other_owner_type::a_member) {} };
int main() {
{ shared_ptr<an_owner<id1> > p1(new an_owner<id1>); shared_ptr<an_owner<id2> > p2(new an_owner<id2>(p1)); }
return boost::report_errors(); }
Yes. The second CTOR is wrong CTOR because p2->p_member points to p1->a_member which leads to an access violation after p1 is released. But I think there is no way to avoid supplying a wrong owner.
Larry Evans wrote:
template<owner_id Id> struct an_owner : public other_id<Id> { typedef other_id<Id> other_type; typedef an_owner<other_type::the_id> other_owner_type; member_type a_member; shared_ptr<member_type> p_member; an_owner(void){} an_owner(shared_ptr<other_owner_type>const & a_other) : p_member(a_other, &other_owner_type::a_member) {} };
You use p_member to keep a ref count to an owner of a_member. In this case the owner won't be deleted until p_member is released. But p_member will be released when the owner is deleted. It's a deadlock because the owner will never be deleted. There must be used another method of storing a ref count to an owner of a_member. That's why member_ptr requires pointees (members and owners) to be derived from a predefned base class. This base class stores a raw pointer to a class owner (which excludes a deadlock mentioned above). It also stores a ref count with predefined add_ref() and release() functions. The difference between member_ptr and intrusive_ptr is that the member_ptr exposes through function get() a pointer of one type and calls add_ref() and release() for a pointer of the second type. Oleg Fedtchenko The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html thread entry http://lists.boost.org/MailArchives/boost/msg73168.php

Oleg Fedtchenko wrote:
Another thing is that it's impossible to use custom add_ref() and release() for an owner the way it is done in intrusive_ptr. I mean a developer of a class must choose one of the shared_ptr and intrusive_ptr (if the last is extended to support a smart member pointers). But we want this class to be used inside owner of any type. And one owner will live with just *delete me* but another owner will require *let me do some processing and then delete me myself*.
Larry Evans wrote:
Can't *let me do some processing and then delete me myself* be done with a custom deleter argument to shared_ptr? I.e. with the CTOR:
template<class Y, class D> shared_ptr(Y * p, D d): px(p), pn(p, d) { detail::sp_enable_shared_from_this( pn, p, p ); }
Ok. Agree. But what about custom add_ref()? Oleg Fedtchenko BoostOleg@yandex.ru http://boostoleg.narod.ru/member_ptr.html recommended thread entry http://lists.boost.org/MailArchives/boost/msg73168.php

Oleg Fedtchenko wrote:
shared_ptr<Tree> Parse( shared_ptr<DocActiveX> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; }
And corresponding ctor that passes over a deletable pointer to an owner (ptrOwner) of a class member (p):
template <class Y, class Z> shared_ptr( Y * p, shared_ptr<Z>& ptrOwner) : px(p), pn(ptrOwner.pn) {...}
But it's good if this class will never be used as an object (nondynamic) data-member inside another owner.
Also it's good when we create shared_ptr using ptrOwner outside a class because we can pass in a shared_ptr of any owner.
Larry Evans wrote:
This is unclear to me. Could you provide an example?
The Parse above is a function-member of a class DocActiveX (I forgot to specify this). Therefore shared_ptr ptrOwner has DocActiveX as its parameter. But if an object of DocActiveX is a member of another class like this: class Superclass { public: DocActiveX doc; }; then parameter ptrOwner in the function DocActiveX::Parse (see above) must be of type shared_ptr<Superclass>: shared_ptr<Tree> Parse( shared_ptr<Superclass> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; } because ptrOwner is what ought to be deleted when all shared_ptr's to m_Tree are released. To resolve this issue the function Parse accepting owner of any type can be written in a templated form: template <class Z> shared_ptr<Tree> Parse( shared_ptr<Z> ptrOwner) { shared_ptr<Tree> ptrMember( &m_Tree, ptrOwner); return ptrMember; } But such a templated function has its own drawbacks, and there is an alternative to it. Please, read this email http://lists.boost.org/MailArchives/boost/msg73234.php on that. The issue about the owner type doesn't raise when shared_ptr to a member is created outside a class containing that member (e.g. inside an actual owner): Tree* Parse() { Tree* ptrMember = &m_Tree; return ptrMember; } void SomeClass::SomeFunc() { ... shared_ptr<Superclass> pSuper(new Superclass); ... shared_ptr<Tree> ptrTreeHere( pSuper->doc.Parse(), pSuper); ptrTreeHere->SomeFunc(); ... } But we can easily by mistake put some wrong owner instead of *pSuper*: shared_ptr<Tree> ptrTreeHere( pSuper->doc.Parse(), this); // it's wrong because *this* cannot be deleted on destructing ptrTreeHere // // only dynamically allocated owner can be passed in the Parse // and *this* can point to an object data-member (nondeletable using // op *delete*) of some other class And the next is clearer, isn't it? And Parse below can have the only instance (as it's not templated). member_ptr<Tree> Parse() { member_ptr<Tree> ptrMember( &m_Tree); return ptrMember; // deletable owner of m_Tree gets into ptrMember // from m_Tree inside its CTOR } void Superclass::SomeFunc() { ... doc.Parse()->SomeFunc(); ... } The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html Oleg Fedtchenko recommended thread entry http://lists.boost.org/MailArchives/boost/msg73168.php

Peter Dimov wrote:
This is a bit harder.
class A { private: WORD m_wData;
private: A();
public: static shared_ptr<A> CreateA() { return shared_ptr<A>(new A); }
friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) { return shared_ptr<WORD>( &this_->m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } };
int main() { shared_ptr<A> pa = A::CreateA(); shared_ptr<WORD> pw = GetData(pa); pa.reset(); std::cout << "*pw: " << *pw << '\n'; }
Parameter this_ in the function (above) friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) is an owner of a data member this_->m_wData. But the owner may be not an immediate class-container-of-the-member or its derivative but any arbitrary class like this: class A { public: WORD m_wData; }; class B { public: A a; friend shared_ptr<WORD> GetData( shared_ptr<B> this_ ) { return shared_ptr<WORD>( &this_->a.m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } }; In this case we must create overridden functions for every owner type to correctly specify pointer to a member. And what about when A::m_wData is private? Don't you think this is the better variant: template<class Z> shared_ptr<WORD> A::GetData( shared_ptr<Z> owner ) { return shared_ptr<WORD>( &m_wData, boost::bind( &shared_ptr<Z>::reset, owner ) ); } Oleg Fedtchenko The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html thread entry http://lists.boost.org/MailArchives/boost/msg72943.php

Oleg Fedtchenko wrote:
But the owner may be not an immediate class-container-of-the-member or its derivative but any arbitrary class like this:
class A { public: WORD m_wData; };
class B { public: A a;
friend shared_ptr<WORD> GetData( shared_ptr<B> this_ ) { return shared_ptr<WORD>( &this_->a.m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } };
In this case we must create overridden functions for every owner type to correctly specify pointer to a member.
Yes.
And what about when A::m_wData is private?
Well, it depends on the actual level of privacy. Clearly a member whose address is always exposed to the world isn't really private, regardless of its access specifier. But that aside, depending on the level of coupling between A and B, B should either be a friend of A, or it must go via an analogous mechanism to obtain WORD* from A*: // in A friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) { return shared_ptr<WORD>( &this_->m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); } // in B friend shared_ptr<WORD> GetData( shared_ptr<B> this_ ) { shared_ptr<A> pa( &this_->a, boost::bind( &shared_ptr<B>::reset, this_ ) ); return GetData( pa ); } Yes, this is not very efficient. But it's encapsulated (which is pretty much the only point of making the data members "private" in this case.) That is, you can change B::a from A a; to shared_ptr<A> pa; without changing the client code, or you could even generate the A dynamically inside GetData. Similarly, you can change A::m_wData to shared_ptr<WORD>. Another shared_ptr advantage is that weak_ptr works.
Don't you think this is the better variant:
template<class Z> shared_ptr<WORD> A::GetData( shared_ptr<Z> owner ) { return shared_ptr<WORD>( &m_wData, boost::bind( &shared_ptr<Z>::reset, owner ) ); }
This is error-prone. I can pass any shared_ptr as an owner, regardless of whether it actually owns *this.

Peter Dimov wrote:
it must go via an analogous mechanism to obtain
WORD* from A*:
// in A friend shared_ptr<WORD> GetData( shared_ptr<A> this_ ) { return shared_ptr<WORD>( &this_->m_wData, boost::bind( &shared_ptr<A>::reset, this_ ) ); }
// in B friend shared_ptr<WORD> GetData( shared_ptr<B> this_ ) { shared_ptr<A> pa( &this_->a, boost::bind( &shared_ptr<B>::reset, this_ ) );
return GetData( pa ); }
Doesn't it bring C++ *polymorphism* to naught? Is it possible to implement the next example using global functions as specified above? class D { private: WORD m_wData; public: D(){ m_wData = 0;} virtual shared_ptr<WORD> GetData( shared_ptr<D> this_ ) { return shared_ptr<WORD>( &m_wData, boost::bind( &shared_ptr<D>::reset, this_ ) ); ); } } class E : public D { private: WORD m_wOtherData; public: virtual shared_ptr<WORD> GetData( shared_ptr<D> this_ ) { return shared_ptr<WORD>( &m_wOtherData, boost::bind( &shared_ptr<D>::reset, this_ ) ); ); } } // usage void SomeFunc( shared_ptr<D> ptrObj) { shared_ptr<WORD> ptrData = ptrObj->GetData( ptrObj); *ptrData = 57; // do the same for all objects } int main() { shared_ptr<D> ptrD(new D); shared_ptr<E> ptrD(new E); SomeFunc( ptrD); SomeFunc( ptrE); } Oleg Fedtchenko The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html thread entry http://lists.boost.org/MailArchives/boost/msg72943.php

Peter Dimov wrote:
No.
in reply to Oleg Fedtchenko's:
Is it possible to implement the next example using global functions as specified above?
class D { private: WORD m_wData; public: D(){ m_wData = 0;}
virtual shared_ptr<WORD> GetData( shared_ptr<D> this_ )
Is it worth using the proposed *member_ptr* smart pointer (e.g. for the tasks discussed in the previous messages or like those described at http://boostoleg.narod.ru/examples.htm) ? The proposed smart pointer member_ptr is described at http://boostoleg.narod.ru/member_ptr.html Oleg Fedtchenko thread entry http://lists.boost.org/MailArchives/boost/msg72943.php

Oleg Fedtchenko wrote:
Peter Dimov wrote:
No.
in reply to Oleg Fedtchenko's:
Is it possible to implement the next example using global functions as specified above?
class D { private: WORD m_wData; public: D(){ m_wData = 0;}
virtual shared_ptr<WORD> GetData( shared_ptr<D> this_ )
Is it worth using the proposed *member_ptr* smart pointer (e.g. for the tasks discussed in the previous messages or like those described at http://boostoleg.narod.ru/examples.htm) ?
I'm not sure. In a situation like the above I'd probably go with enable_shared_from_this. member_ptr also seems to require a base class, so it's a tie, except that the returned pointer is a shared_ptr: more "standard" and supports weak pointers. http://boost.org/libs/smart_ptr/enable_shared_from_this.html http://boost.org/libs/smart_ptr/sp_techniques.html#from_this If D is intrusively counted, I can also return a shared_ptr that does intrusive_ptr_release on it, similar to: http://boost.org/libs/smart_ptr/sp_techniques.html#intrusive but storing a different pointer in the deleter, like: shared_ptr<WORD> pw( &this->m_wData, bind( intrusive_deleter<D>(), this ) ); It seems that your member_ptr can be emulated with shared_ptr<WORD> pw( &this->m_wData, bind( &member_ptr_owner_base::release, this ) ); but I may be wrong; I looked at it only briefly.
participants (5)
-
Alexander Nasonov
-
David Abrahams
-
Larry Evans
-
Oleg Fedtchenko
-
Peter Dimov