Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter/storage type for a C++11 lambda

In C++11, how do you declare a function that takes a lambda expression as an argument? I can find plenty of resources online for declaring lambdas or taking them as template parameters, but what I'd really like to do is be able to make use of lambdas as easy-to-declare callback handlers, similar to what's made possible by closures in JavaScript and code blocks in Objective-C.

Essentially, the classic C++ construct I want to replace with a lambda is something like:

class MyCallback {
public:
    virtual ~MyCallback() {}
    virtual void operator(int arg) = 0;
};

void registerCallback(const std::shared_ptr<MyCallback> &);

void foo(void) {
    int a, b, c;
    class LocalCallback: public MyCallback {
        int a, b, c;
    public:
        LocalCallback(int a, int b, int c): a(a), b(b), c(c) {}
        void operator(int arg) { std::cout << (a+b+c)*arg << std::endl; }
    };
    registerCallback(std::shared_ptr<MyCallback>(new LocalCallback(a,b,c)));
}

which would be simplified into:

void registerCallback(/* WHAT GOES HERE? */);

void foo(void) {
    int a, b, c;
    registerCallback([=](int arg){std::cout << (a+b+c)*arg << std::endl; })
}

So, what goes where I have written /* WHAT GOES HERE? */?

EDIT: This is for the purpose of storing a callback to be called back later, rather than for it being immediately consumed and called.

like image 615
fluffy Avatar asked May 23 '12 20:05

fluffy


2 Answers

Usually const std::function<void(int)> & or std::function<void(int)>.

I'm not sure what the verdict is on whether std::function should be passed by const reference or by value. Probably by value is fine, especially since you're going to copy it anyway to store.

In case it isn't clear in the middle of all that syntax, void(int) is a function type, and std::function<T> means approximately, "a functor with the same signature as functions of type T".

Lambdas themselves have anonymous types. There is no way to name the type of your lambda expression, and the types of different lambda expressions with the same signature are different:

auto foo = [=](int arg){std::cout << (a+b+c)*arg << std::endl; };
auto bar = [=](int arg){std::cout << (a+b+c)*arg << std::endl; };
// foo and bar have different types, accessible as decltype(foo), decltype(bar)

Hence the need for std::function, which basically is a type-erasing wrapper to gather together different functors with the same signature into a common type. It's the bridge between static polymorphism with templates, and the dynamic polymorphism you need if you want to register a callback, store it for later, and then call it without having "remembered" the original type.

like image 185
Steve Jessop Avatar answered Oct 27 '22 01:10

Steve Jessop


void registerCallback(const std::function<void(int)>& callback);
like image 26
nosid Avatar answered Oct 27 '22 01:10

nosid