Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write an anonymous function / lambda that passes itself as a callback?

Tags:

c++

c++11

I am learning boost::asio and C++11 simultaneously. One of my test programs, which is actually an adaptation of one of the samples given in the boost::asio tutorial is the following:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

class printer {

// Static data members
private:
    const static boost::posix_time::seconds one_second;

// Instance data members
private:
    boost::asio::deadline_timer timer;
    int count;

// Public members
public:
    printer(boost::asio::io_service& io)
        : timer(io, one_second), count(0) {

        std::function<void(const boost::system::error_code&)> callback;
        callback = [&](const boost::system::error_code&) { // critical line
            if (count < 5) {
                std::cout << "Current count is " << count++ << std::endl;

                timer.expires_at(timer.expires_at() + one_second);
                timer.async_wait(callback);
            }
        };

        timer.async_wait(callback);
    }

    ~printer() {
        std::cout << "Final count is " << count << std::endl;
    }
};

const boost::posix_time::seconds printer::one_second(1);

int main() {
    boost::asio::io_service io;
    printer p(io);
    io.run();

    return 0;
}

When I run this program, I get a segmentation fault. I do understand why I get the segmentation fault. After the constructor is done running, the constructor's callback variable goes out of scope, and the lambda's callback variable, which is a reference to the constructor's callback variable, becomes a dangling reference.

So I modify the critical line with:

        callback = [callback, &](const boost::system::error_code&) { // critical line

Then compile it, run it, and get a bad function call error. Again, I do understand why I get the bad function call error. Within the lambda's scope, the constructor's callback variable has still not been assigned any value to, so it for all practical purposes is a dangling function pointer. Hence, the lambda's callback variable, which is a copy of the constructor's callback variable, is also a dangling function pointer.


After thinking about this problem for a while, I realized that what I truly need is that the callback be able to refer to itself using a function pointer, not a reference to a function pointer. The sample achieved this by using a named function as callback, instead of an anonymous one. However, passing named functions as callbacks is not very elegant. Is there any way to get an anonymous function have a function pointer to itself as a local variable?

like image 554
pyon Avatar asked Dec 14 '11 05:12

pyon


Video Answer


1 Answers

There are several alternatives:

  • Stop using a lambda. You don't have to use them for everything, you know. They cover a lot of cases, but they don't cover everything. Just use a regular old functor.
  • Have the lambda store a smart pointer to a dynamically allocated std::function that stores the lambda. For example:

    auto pCallback = std::make_shared<std::function<void(const boost::system::error_code&)>>();
    auto callback = [=](const boost::system::error_code&) { // critical line
        if (count < 5) {
            std::cout << "Current count is " << count++ << std::endl;
    
            timer.expires_at(timer.expires_at() + one_second);
            timer.async_wait(pCallback.get());
        }
    };
    *pCallback = callback;
    
like image 146
Nicol Bolas Avatar answered Nov 12 '22 14:11

Nicol Bolas