Lección 18 · 18 de 20
std::function y callbacks · C++
Construya un sistema de eventos en C++ con std::function: registre lambdas, funciones y métodos como callbacks y llámelos al emitir cada evento.

Un callback es una función que se entrega a otra parte del programa para que la llame cuando ocurra algo. Así funcionan los botones de una interfaz: en las aplicaciones de escritorio con FLTK que genera DMarlon Studio, cada pulsación llama a la función que se le ha asociado.
`std::function<void(const std::string&)>` puede guardar cualquier cosa que se pueda llamar con esa firma: una lambda, una función normal o un objeto con operator(). using Handler = ... le da un nombre, y handlers_ relaciona cada nombre de evento con su lista de callbacks.
on() registra un callback para un evento y emit() llama, en orden, a todos los de ese evento. Para click hay dos: una lambda que cuenta los clics capturando clicks por referencia y la función libre writeLog, que encaja porque tiene la misma firma.
Para llamar a un método de un objeto se usa una lambda que lo captura: [&cart](const std::string& p) { cart.add(p); }. El carrito suma un artículo con cada add. Al emitir close, que no tiene callbacks, el bucle no encuentra nada y no ocurre nada más.
Cuidado con las capturas por referencia: el bus guarda las lambdas, así que cart y clicks tienen que seguir vivos mientras se puedan emitir eventos. Aquí todo vive en main y no hay problema.
Código · 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}Salida · 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
Para recordar
- Un callback es una función que otro código llamará más tarde.
std::function<R(Args...)>guarda cualquier invocable con esa firma.- Lambdas, funciones libres y objetos con
operator()valen igual. - Para un método, capture el objeto en una lambda.
- Lo que captura por referencia debe vivir más que el callback.
Ejercicio
Añada un método off(event) que borre todos los callbacks de un evento. Llame a bus.off("click") y emita otro clic para comprobar que ya no se escribe nada.