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?
Lambda function are anonymous functions. Callback can be named or anonymous. So all lambda functions can be a callback function but not all callback function can be lambda function.
The third argument, callback , is a function that you can call in non-async handlers to send a response. The callback function takes two arguments: an Error and a response. When you call it, Lambda waits for the event loop to be empty and then returns the response or error to the invoker.
All lambdas are inline. Not all calls to them are necessarily inlined.
In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called as a Callback function. In C, a callback function is a function that is called through a function pointer. In C++ STL, functors are also used for this purpose.
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:
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With