Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Capturing std::function objects in lambda

Below code fails with BAD_ACCESS when I call s_capture_void_int() in the last line and I do not understand why. I suppose that when I assign lambda expression to a global variable it supposed to copy itself together with captured values. So in my understanding dangling references should not appear. But it looks like I'm missing something.

std::function<void()> s_capture_void_int;
void capture_void_int (const std::function<void(int)>& param)
{
    s_capture_void_int = [param]() {
        param(1);
    };
}
void capture_local_lambda()
{
    auto local_lambda = [](int) {
    };
    s_capture_void_int = [local_lambda]() {
        local_lambda(1);
    };
}
BOOST_AUTO_TEST_CASE( test_lambda_captures )
{
    //Case 1: this works
    auto func2 = [](int){};
    {
        std::function<void(int)> func2_fn(func2);
        s_capture_void_int = [func2_fn]() { func2_fn(1); };
    }
    s_capture_void_int();

    //case 2: even this works.
    capture_local_lambda();
    s_capture_void_int();

    //case 3: but this fails.
    auto func3 = [](int){};
    {
        std::function<void(int)> func3_fn(func3);
        capture_void_int(func3_fn);
    }
    s_capture_void_int(); //<- it crashes here
}

I don't understand two things here:

  • If crash happen because of func3_fn goes out of scope then why case 1 and 2 works?
  • If I change this code to std::function (note no parameter) then it works ok. Could it be a compiler bug?
like image 591
averbin Avatar asked Oct 20 '22 19:10

averbin


1 Answers

For anybody who come across same problem. This is indeed a compiler bug and I found simple and stupid workaround. Workaround is not tested but at least my program does not segfault right away on a first call to std::function. Problem manifest itself with clang shipped with Xcode 5.0.2 and 5.1 compiler. gcc 4.8 and possibly stock clang does not have this problem. Simplest possible program to trigger problem:

#include <iostream>
#include <functional>

std::function<void()> buggy_function;
/*
void workaround (const std::function<void(int)>& param)
{
    auto a = [&,param]() {
        param(1);
    };
}
*/
void trigger_bug (const std::function<void(int)>& param)
{
    buggy_function = [&,param]() {
        param(1);
    };
}

int main(int argc, const char * argv[])
{
    auto func3 = [](int){};
    std::function<void(int)> func3_fn(func3);
    trigger_bug(func3_fn);
    buggy_function();
    return 0;
}

If you uncomment 'workaround' function it magically start working. Order of functions is important, workaround function have to be before any other functions using std::function. If you put 'workaround' below 'trigger_bug' then it stop working.

like image 200
averbin Avatar answered Oct 30 '22 02:10

averbin