Search code examples
c++boostboost-bindboost-function

Does copying a boost::function also copy the closure?


Say I have a function like this:

void someFunction(const ExpensiveObjectToCopy&);

If I make a boost::function out if it, that function will store its own cloned copy of the object in its closure:

boost::function<void()> f = boost::bind(someFunction, x);  // <-- f saves a copy of x

Now if I start passing f around, will the boost::function copy constructor copy that object again each time, or does each function share the same closure? (i.e. like this)

boost::function<void()> f2 = f;
callSomeFunction(f);
etc.

Solution

  • From what I can find (Judging from a cursory reading of the not exactly simple source and some experiments) it will copy the cloned object each time. It might be unnecessary in case of the function taking it's argument by const &, but in general the object might be mutated by the function. If the object is expensive to copy, wouldn't it make sense to capture it by reference (boost::ref or boost::cref come to mind) or, if the original object doesn't exist at the point of invocation, capture a boost::shared_ptr and write a adapter method, which unpacks the smartpointer and calls someFunction?

    Edit: From experimentation not only will it copy construct that object whenever the boost::function is copied, but it will also copy several times inside boost::bind. I tested using the following code using boost 1.45 under mingw 32 with gcc 4.6 and -O2 ( and -std=c++0x):

    struct foo_bar {
        std::vector<int> data; //possibly expensive to copy
        foo_bar()
        { std::cout<<"default foo_bar "<<std::endl; }
        foo_bar(const foo_bar& b):data(b.data)
        {  std::cout<<"copy foo_bar "<<&b<<" to "<<this<<std::endl; }
        foo_bar& operator=(const foo_bar& b) {
            this->data = b.data;
            std::cout<<"asign foo_bar "<<&b<<" to "<<this<<std::endl;
            return *this;
        }
        ~foo_bar(){}
    };
    
    void func(const foo_bar& bar) { std::cout<<"func"<<std::endl;}
    
    int main(int, char*[]) {
        foo_bar fb;
        boost::function<void()> f1(boost::bind(func, fb));
        std::cout<<"Bind finished"<<std::endl;
        boost::function<void()> f2(f1);
        std::cout<<"copy finished"<<std::endl;
        f1();
        f2();
        return 0;
    }
    

    The resulting output is as followed:

    default foo_bar
    copy foo_bar 0x28ff00 to 0x28ff10
    copy foo_bar 0x28ff10 to 0x28ff28
    copy foo_bar 0x28ff28 to 0x28ff1c
    copy foo_bar 0x28ff1c to 0x28ff34
    copy foo_bar 0x28ff34 to 0x28fed4
    copy foo_bar 0x28fed4 to 0x28fee4
    copy foo_bar 0x28fee4 to 0x28fef4
    copy foo_bar 0x28fef4 to 0x28fe14
    copy foo_bar 0x28fe14 to 0x28fe24
    copy foo_bar 0x28fe24 to 0x28fe34
    copy foo_bar 0x28fe34 to 0x6a2c7c
    Bind finished
    copy foo_bar 0x6a2c7c to 0x6a2c94
    copy finished
    func
    func
    

    So the copy constructor was called for the creation of f2 once and 11 times for the binding and assignment to f1. Since the first object is created on the stack and the addresses of the copies are very close to that and slightly increasing, it seems that the binding process goes through a lot of functions, which the compiler doesn't inline in this case and which each pass the object along by value. Using just boost::bind without saving the result anywhere:

    int main(int, char*[]) {
        foo_bar fb;
        boost::function<void()> f1(boost::bind(func, fb));
        return 0;
    }
    
    default foo_bar
    copy foo_bar 0x28ff00 to 0x28ff10
    copy foo_bar 0x28ff10 to 0x28ff28
    copy foo_bar 0x28ff28 to 0x28ff1c
    copy foo_bar 0x28ff1c to 0x28ff34
    copy foo_bar 0x28ff34 to 0x28fef4
    

    So five copies just to bind the object. So I would generaly avoid capturing anything which has at least moderate copy costs per value in any even remotely performance sensitive parts of the code. In comparison gccs std::tr1::bind and std::bind perform much better (in conjunction with std::tr1::function / std::function) (code is basically identical to the first testcode, just substitute boost:: with std::tr1:: respectively std:::

    std::tr1::bind with std::tr1::function:
    default foo_bar
    copy foo_bar 0x28ff10 to 0x28ff28
    copy foo_bar 0x28ff28 to 0x28ff34
    copy foo_bar 0x28ff34 to 0x28ff04
    copy foo_bar 0x28ff04 to 0x652c7c
    Bind finished
    copy foo_bar 0x652c7c to 0x652c94
    copy finished
    func
    func
    
    std::bind with std::function:
    default foo_bar
    copy foo_bar 0x28ff34 to 0x28ff28
    copy foo_bar 0x28ff28 to 0x3c2c7c
    Bind finished
    copy foo_bar 0x3c2c7c to 0x3c2c94
    copy finished
    func
    func
    

    I assume std::bind either passes by const ref for internal invocations, or is written in a way which is more friendly to gccs inliner to inline some in and eliminate redundant copy constructors. tr1::bind still gets better optimized then boost::bind, but still far from optimal.

    Of course as always with such a kind of tests YMMV with different compile flags/compilers