enable_shared_from_this: boost vs. std

If you are a modern C++ developer, then you are probably using some kind of smart pointer implementation. The boost C++ libraries offer one possible solution (among many other useful features) and are generally held in high regards in the C++ community. With the latest C++11 standard, some of those ideas found their way into the standard library bundled with your C++ compiler. At some point, you very likely run into a situation where you need a shared_ptr of one of your classes, but only have a raw pointer or this available.

This is where enable_shared_from_this comes in. Boost and the standard C++ library provide this feature and they both have a very important prerequisite for this to work.
First of all, why would one actually need a shared_ptr to an object that is not yet managed by a smart pointer? The situation I mostly run into, but not exclusively, is when your class wants to call a (helper) method of another class which only accepts shared pointers.

enum diagnostic_status
{
    excellent_shape,
    engine_failure,
    car_sucks
};

class car;
class car_diagnostic
{
public:
 diagnostic_status check(shared_ptr vehicle)
 {
 // Do something and return some status code.
 return car_sucks;
 }
};

class car {
public:
 void start()
 {
 auto status = m_diagnostic.check([now what?]);
 switch (status) {
 // Decide on what to do.
 }
 }

private:
 car_diagnostic m_diagnostic;
};

int
main(int argc, char** argv)
{
 car pollution;
 pollution.start();
 return 0;
}

As you can see in the line that wants to call into the car_diagnostic.check() method, where’s the parameter supposed to come from? The first thing that might come to your mind is to just wrap this in a shared_ptr like so.

class car {
public:
    void start()
    {
        shared_ptr temp(this);
        auto status = m_diagnostic.check(temp);
        switch (status) {
            // Decide on what to do.
        }
    }

private:
 car_diagnostic m_diagnostic;
};

That’ll certainly compile. But it won’t work. Think about it, you are giving your instance pointer to be managed by a smart pointer. What do you suspect will happen once “temp” goes out of scope? It’ll wreck your car. (See what I did there?)

Enter (Sandman) enable_shared_from_this. By deriving car from enable_shared_from_this, we gain access to the shared_from_this() method that returns exactly what we need. Let’s augment our code with that knowledge.

class car : public enable_shared_from_this {
public:
    void start()
    {
        auto status = m_diagnostic.check(shared_from_this());
        switch (status) {
            // Decide on what to do.
        }
    }

private:
 car_diagnostic m_diagnostic;
};

As you can see, now that car inherits from enable_shared_from_this we are able to actually get a shared_ptr to a car instance and pass that to the diagnostic check. Or can we? Well, that code compiles already, but when you run it it’ll wreck your car, kill the passengers and destroy the world around it.

Let’s start with what the C++ standard has to say and take a look at the documentation.

Note that prior to calling shared_from_this on an object t, 
there must be a std::shared_ptr that owns t.

Unfortunately it doesn’t specify what happens if that is not the case and the most recent C++ draft, section 20.8.2.5, doesn’t say anything about it either (I’m not inclined to buy the officially released version). So I debugged to find the location where it explodes. In Microsoft’s implementation of the standard library (or rather Dinkumware, because that’s what they use) it happens in the following method of _Ptr_base, the base class for shared_ptr and weak_ptr.

void _Reset(_Ty *_Other_ptr, _Ref_count_base *_Other_rep, bool _Throw)
 {
 // take _Other_ptr through _Other_rep from weak_ptr if not expired
 // otherwise, leave in default state if !_Throw,
 // otherwise throw exception
 if (_Other_rep && _Other_rep->_Incref_nz())
  _Reset0(_Other_ptr, _Other_rep);
 else if (_Throw)
  _THROW_NCEE(bad_weak_ptr, 0);
 }

“_Other_rep” is a nullptr and „_Throw“ defaults to true which results in a bad_weak_ptr exception to be thrown. There you have it, that’s your nuke. What about boost? This one is easy because it is mentioned in the documentation.

Throws: bad_weak_ptr when no shared_ptr owns *this. 

This means two things:

  1. Your code should be ready to catch this kind of exception.
  2. You can only call shared_from_this if there’s already a shared_ptr to the instance you’re calling it on. Here’s the updated code that not only compiles but also runs.
int
main(int argc, char** argv)
{
    try {
        auto pollution = make_shared();
        pollution->start();
        return 0;
    }
    catch (bad_weak_ptr) {
        return 1;
    }
}

For this example I specified using namespace std; at the top of my source file to make it more concise and also to show that the code can be used interchangeably with boost. Just replace it with using namespace boost; if you prefer that or don’t yet have access to a C++11 compliant compiler and standard library.

So, in the end it isn’t about “boost vs. std” but rather “boost and std”. Well, regarding the documentation there’s a little “versus”. Anyway, that’s how it works.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.