Passing and storing lambda function as callbacks

24,902

Solution 1

This is a perfectly valid approach to storing event handlers.

However, I would like to point out some details regarding the signature of your function for adding a callback. You worry about passing it by value vs by reference. In your example, you currently have:

void On(EventType OnEventType,std::function<void()>&& Callback)

This is good, as long as you will only ever bind this to rvalues. However, unless there are particular reasons why you want to disallow it, I would recommend that you always have a method which accepts parameters by value or lvalue reference and add the rvalue reference version as a complement, if deemed necessary.

Not having a method which takes an lvalue reference means your code will currently fail to compile given this:

std::function<void()> func([](){/*something clever*/});
// Do something necessary with func, perhaps logging or debug prints.
Button->On(EventType::Click, func);

For simplicity, whenever you're choosing how to pass a value, you can simply follow these guidelines in general:

  • If you need a copy of or intend to modify the value sent in, without wanting to change the actual object passed: pass by value.
  • If you intend to modify the value sent in, and want these changes to affect the actual object passed: pass by reference.
  • If you do not want to change the object passed, but believe it is beneficial to avoid copying: pass by const reference.
  • If you take parameters by value, reference or const reference, and believe there are valuable optimizations which can be achieved using the knowledge that the input parameter is a temporary: also allow to pass by rvalue reference.

Solution 2

Yes. Functions are either raw function pointers or light weight classes that their copy constructor has no side effect, so their copy must act as original object, so this approach is completely ok. But why you pass object by value and then move it to your original container, you can pass reference and then copy it to your container and have an overloaded function that accept an r-value reference (should not be so important).

Share:
24,902
Grapes
Author by

Grapes

Updated on September 30, 2020

Comments

  • Grapes
    Grapes over 3 years

    I was wondering if this would be an accepted approach to writing callbacks:

    Storing callbacks:

    struct EventHolder {
        std::function<void()> Callback;
        EventTypes::EventType Type;
    };
    std::vector<Events::EventHolder> EventCallbacks;
    

    Method definition:

    void On(EventType OnEventType,std::function<void()>&& Callback)
    {
        Events::EventHolder NewEvent;
        NewEvent.Callback=std::move(Callback);
        NewEvent.Type=OnEventType;
        EventCallbacks.push_back(std::move(NewEvent));
    }
    

    Binding event:

    Button->On(EventType::Click,[]{
        // ... callback body
    });
    

    My biggest question would be regarding passing the Callback by value. Is this a valid approach?