C/C++ Arena

Step 7 of 7

Challenge: callbacks with std::function

Sometimes you want to store "something to call later": a button's click handler, a callback when a download finishes, a list of listeners. In C that's a function pointer plus a void * for extra data. C++ has a type-safe version: std::function<R(Args...)> (from <functional>) holds any callable with that signature: a plain function, a lambda (including one with captures), or an object with operator().

#include <functional>
#include <iostream>
#include <string>
#include <vector>

int twice(int x) { return 2 * x; }

int main() {
    std::vector<std::function<int(int)>> steps;
    int offset = 100;
    steps.push_back(twice);                                   // a plain function
    steps.push_back([](int x) { return x + 1; });             // a lambda
    steps.push_back([offset](int x) { return x + offset; });  // a lambda with a capture

    int value = 5;
    for (const auto& f : steps) value = f(value);
    std::cout << value << "\n";

    std::function<void(const std::string&)> on_done;
    if (!on_done) std::cout << "no handler yet\n";
    int calls = 0;
    on_done = [&calls](const std::string& file) { calls++; std::cout << "got " << file << "\n"; };
    on_done("a.zip");
    on_done("b.zip");
    std::cout << calls << " calls\n";
}
111
no handler yet
got a.zip
got b.zip
2 calls

How it works

Designing the event bus

Each subscription needs three things: its id, its event name, and its handler. A small struct in a std::vector keeps them in registration order, which is the order emit must use. Keep a counter member for the next id, starting at 1.

Your turn: build an EventBus where handlers subscribe to named events:

Previous: Iterator invalidation