Travis Gockel
Travis Gockel

Reputation: 27693

enable_shared_from_this: Sharing the use count in member

Say I have two classes: Thing and Holder:

struct Thing :
        std::enable_shared_from_this<Thing>
{
    std::shared_ptr<Thing> self()
    {
        return shared_from_this();
    }
};

struct Holder :
        std::enable_shared_from_this<Holder>
{
    Thing t;
};

int main()
{
    auto h = std::make_shared<Holder>();
    // will throw std::bad_weak_ptr
    h->t.self();
}

I would like Thing's internal weak_ptr to use the shared count for Holder, instead of requiring Holder to keep a shared_ptr to Thing. In the opposite direction, Thing needs to be able to exist without knowing what a Holder is (std::make_shared<Thing>().self() should still work). Is there a decent way to accomplish what I am looking for?


Things that Don't Work

Saying something like std::shared_ptr<Thing> t(h, &h->t); does not work, since the aliasing constructor for a std::shared_ptr does not use the enable_shared_from_this visitor.

With Boost, one could hack around this by overloading sp_enable_shared_from_this; however, with g++'s implementation, it isn't obvious how to do the equivalent with __enable_shared_from_this_helper (not to mention we're jumping into "DON'T DO THAT" land).


Boost Workarounds

When in Boost, it isn't terribly difficult to work around this issue by calling _internal_accept_owner directly:

int main()
{
    auto h = boost::make_shared<Holder>();
    h->t._internal_accept_owner(&h, &h->t);
    h->t.self();
}

With boost::enable_shared_from_this2, one can embed the needed code in the constructor of Holder like so:

Holder()
{
    boost::shared_ptr<Holder> self = shared_from_this();
    t._internal_accept_owner(&self, &t);
}

I'm looking to do the equivalent with std::enable_shared_from_this.

Upvotes: 3

Views: 1371

Answers (2)

Luc Danton
Luc Danton

Reputation: 35469

If you don't mind it then you can model the composition of a Thing inside a Holder via private inheritance rather than with a member object. Then inside a member of Thing you can 'retrieve' the enclosing Holder with a static_cast<Holder&>(*this) downcast -- and after that use shared_from_this to get access to the shared count.

However, because it is a private base of Holder, Thing needs a friend declaration to be able to do the downcast. Since in addition to that the dependency between the two classes is circular , that makes for some heavy coupling.

Upvotes: 1

obmarg
obmarg

Reputation: 9569

Ok, now that I understand what you're asking, here's an alternative. You could pass in a shared_ptr< Holder > to Thing, allowing it to create it's own aliased shared_ptr. Something like this:

struct Thing : std::enable_shared_from_this<Holder>
{
     Thing() {};

     Thing( std::shared_ptr< Holder >& holderPtr ) :
     m_holder( holderPtr )
     {
     }

     std::shared_ptr< Holder > m_holder;


     std::shared_ptr<Thing> self()
     {
         try 
         { 
              return shared_from_this(); 
         }
         catch( const std::bad_weak_ptr& )
         {
              return std::shared_ptr< Thing >( m_holder, this );    
         }
     }
};

struct Holder : std::enable_shared_from_this<Holder>
{
    Thing t;
};

Personally seems like it'd be better to just hold a shared_ptr< Thing > inside Holder, but this seems like it'd work if you preferred it. Apart from the circular reference between Holder and Thing. Guess you could use a weak_ptr< Holder > inside Thing instead.

Upvotes: 2

Related Questions