Lesson 18 · 18 of 20
std::function and callbacks · C++
Build an event system in C++ with std::function: register lambdas, functions and methods as callbacks and call them when each event fires.

A callback is a function you hand to another part of the program so it can call it when something happens. That is how buttons in a user interface work: in the FLTK desktop apps that DMarlon Studio generates, each click calls the function attached to it.
`std::function<void(const std::string&)>` can hold anything that can be called with that signature: a lambda, a plain function or an object with operator(). using Handler = ... gives it a name, and handlers_ maps each event name to its list of callbacks.
on() registers a callback for an event and emit() calls all of that event's callbacks, in order. click has two: a lambda that counts clicks by capturing clicks by reference, and the free function writeLog, which fits because it has the same signature.
To call a method on an object, use a lambda that captures it: [&cart](const std::string& p) { cart.add(p); }. The cart adds one item on each add. When close is emitted, there are no callbacks for it, so the loop finds nothing and nothing else happens.
Watch out for captures by reference: the bus stores the lambdas, so cart and clicks must stay alive for as long as events can be emitted. Here everything lives in main, so all is well.
Code · main.cpp
#include <functional>#include <iostream>#include <map>#include <string>#include <vector>// A small event system: each event name keeps a list of callbacks.class EventBus { using Handler = std::function<void(const std::string&)>; // any callable with this signature std::map<std::string, std::vector<Handler>> handlers_;public: void on(const std::string& event, Handler h) { handlers_[event].push_back(h); } void emit(const std::string& event, const std::string& data) { std::cout << "emit " << event << "\n"; for (const Handler& h : handlers_[event]) h(data); }};void writeLog(const std::string& data) { std::cout << " [log] " << data << "\n"; }struct Cart { int items = 0; void add(const std::string& p) { std::cout << " [cart] " << p << ", " << ++items << " item(s)\n"; }};int main() { EventBus bus; Cart cart; int clicks = 0; bus.on("click", [&clicks](const std::string& button) { std::cout << " [ui] click " << ++clicks << ": " << button << "\n"; }); bus.on("click", writeLog); // a plain function fits too bus.on("add", [&cart](const std::string& p) { cart.add(p); }); // calls a method bus.emit("click", "Buy now"); bus.emit("add", "Headphones"); bus.emit("add", "Charger"); bus.emit("close", "nobody listens"); // no handlers: nothing runs}Output · c++ -std=c++17 main.cpp -o main && ./main
emit click [ui] click 1: Buy now [log] Buy now emit add [cart] Headphones, 1 item(s) emit add [cart] Charger, 2 item(s) emit close
Key points
- A callback is a function that some other code will call later.
std::function<R(Args...)>holds any callable with that signature.- Lambdas, free functions and objects with
operator()all fit. - For a method, capture the object in a lambda.
- Anything captured by reference must outlive the callback.
Exercise
Add an off(event) method that removes every callback for an event. Call bus.off("click") and emit another click to check that nothing is printed any more.