Out of Scope boost::bind

Thanks to the guys at StackOverflow I was able to come up with a clear example to demonstrate my point. The idea originated from unclear copying of member variables (in this context a function pointer) and as a result to very cryptic core dumps. It is not too uncommon that a object is no longer valid even through there are pointers still referencing it. This has been know for a long time now and the C++ standard library solves it by providing the programmer with many different types of smart pointers. And yes, other languages solve this by not exposing pointers to the developer. And as we know, the bug is usually located between the chair and the monitor, which makes this idea easy to follow.

What one might or might not realize, is that the bind function from the boost library enables the user to keep a function pointer to a member function. It is not too dangerous if it is pointing a static function, that can’t go out of scope (before other non-static objects). But what happens when you refer to a local object? It’s methods will always be available by memory location (ptr) as they reside in the code memory section. But this by no means applies to the object’s variables that these functions might use. Static functions can’t use non-static variables, but non-static functions can.

Boost::bind can be used to either pre-define a set of function parameters or as in this case, to convert a member function to a boost_function. Here is an excellent code example, that shows my point:


#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

#include <iostream>

struct X
{
    int foo() const
    {
        return 42;
    }
    virtual ~X() {
        std::cout << "I'm stepping out here\n";
    }
};

int weak_call(int (X::*ptmf)() const, boost::weak_ptr<X> const& wp)
{
    auto locked = wp.lock();
    if (!locked)
        throw boost::bad_weak_ptr();

    return ((*locked).*ptmf)();
}

int main()
{
    boost::function<int()> bound_foo;

    {
        auto x = boost::make_shared<X>();
        bound_foo = boost::bind(weak_call, &X::foo, boost::weak_ptr<X>(x));

        std::cout << "Bound foo returns: " << bound_foo() << "\n";
    }

    std::cout << "Bound foo returns: " << bound_foo() << "\n";
}

The important part is boost::bind(weak_call, &X::foo, …[35] As you can see, you bind the boost_function -bound_foo-  to your local object’s -X- function -foo-. What happens next is. that control leaves the local scope and X gets destructed [38]. But we still have a pointer that enables us to call the, now deallocated, function foo [40]. The good thing about the weak pointer is, that you can explicitly ask about its validity. As of the second bound_foo call,  the handling bound to wp.lock() throws an exception because calling X.foo no longer is valid [22-24]. Should one use simple C style pointers, or native boost::functions, such a check will not be performed and an invalid function will be called. I will leave the consequences to your imagination. It will probably include some weekend overtime.

Of course in this sandbox environment, it is easy to spot the misuse. But imagine that you have your function_pointer as a member variable that gets copied along with all the other stuff that your copy constructor handles. Or that you have a weird form of a template pattern and the child stores pointers to its parents methods. You might as well pass the function to another process. Spotting the bug in these cases, can make your head spin.

I am not saying that binding functions to local members should be avoided. Sometimes there might be no other choice. But commit this example to memory and think about the consequences the next time you see such a use case. Correct use of object oriented design and polymorphism will reduce your needs for binding member functions.

Just because C++ enables you to do something, does not mean that you should!


If you enjoyed this post, then make sure you subscribe to my Newsletter and/or Feed.

Facebooktwittergoogle_plusredditpinterestlinkedin