c++c++11boostboost-signals

Using boost::signals2::trackable with lambdas


I am using a pattern like this, C++11:

class FooViewController {
    void build() {
        auto label = ...

        network->doWork([] (const Result& r) {
             label->setText(r.text);
        });
    }
}

FooViewController may deconstruct before doWork completes, causing crashes. Looking at boost::signals2, I'm thinking of using boost::signals2::trackable, which works great for my single threaded purposes, with the benefit that I do not have to hold and manage my connections directly, however I'm not sure how to get such a solution working with lambdas.

Here is a working lambda free version:

class Foo : public boost::signals2::trackable {
public:
    void bar() {
        printf("Fire!");
    }
};


Usage:

    boost::signals2::signal<void()> signal;
    {
        Foo test;
        signal.connect(boost::bind(&Foo::bar, &test));
        signal();
    }
    signal();

Output:

    Fired!
    // Note a second 'Fired!' did not occur, which is correct behavior

Two goals:

1-- I'd like to do something like:

signal.connect(boost::bind([] {
    printf("Fired!");
}, &test));

Which would NOT call the lambda after test is torn down.

2-- I do not want to directly manage the connection objects returned by .connect.


Solution

  • Found the answer referencing trackable_test.cpp:

    struct short_lived : public boost::signals2::trackable {
        ~short_lived() {
            cout << "I'm dying...!" << std::endl;
        }
    };
    
    void main() {
        typedef boost::signals2::signal<void()> sig_type;
        sig_type s1;
    
        short_lived* shorty = new short_lived();
        s1.connect(boost::bind<void>([](const short_lived*) {
            cout << "Fire!" << std::endl;
        }, shorty));
        s1();
        delete shorty;
    
        s1();
    }
    

    Output

    Fire!
    I'm dying...!
    

    ...and a multiple params example (boost::bind refresher):

    typedef boost::signals2::signal<void(int)> sig_type;
    
    // ...same...
    
    s1.connect(boost::bind<void>([](const short_lived*, int cannon) {
        cout << "Fire " << cannon << "!" << std::endl;
    }, shorty, _1));
    s(1);
    delete shorty;
    s(2);
    

    Output

    Fire 1!
    I'm dying...!