Fun With C++ Callbacks

I did some experiments with callbacks based on a snippet from ideone.com.

EventManagerBuild&Run
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <algorithm>
#include <functional>

class EventManager
{
public:
    template<class F>
    void AddClient(F client)
    {
        clients.emplace_back(new EventClient<F>(std::move(client)));
    }
    
    void Emit()
    {
        std::for_each(clients.begin(), clients.end(), [](auto& client){ client->Respond(); });
    }
    
private:
    struct IEventClient
    {
        virtual ~IEventClient() { }
        virtual void Respond() = 0;
    };
    
    template<class F>
    struct EventClient : IEventClient
    {
        F f;
        EventClient(F&& f) : f(std::move(f)) { }
        void Respond() override { f(); }
    };
    
    std::vector<std::unique_ptr<IEventClient>> clients;
};

struct Foo { void operator()() const { std::cout << "Foo\n"; } };

struct Bar { int operator()() { std::cout << "Bar\n"; return 0; } };

struct Baz
{
    Baz() { id = ID++; obj_valid = true; std::cout << "ctor(id=" << id << ")\n"; };
    ~Baz() { std::cout << "dtor(id=" << id << ")\n"; obj_valid = false; };
    
    //Baz(const Baz& b) { id = b.id; obj_valid = b.obj_valid; std::cout << "cctor(id=" << id << ")\n"; };
    Baz(Baz&& b) { id = std::move(b.id); obj_valid = std::move(b.obj_valid); b.id = -1; b.obj_valid = false; std::cout << "mctor(id=" << id << ")\n"; };
    
    Baz(const Baz& b) = delete;
    Baz& operator=(const Baz&) = delete;
    
    void f(const std::string& s) const { std::cout << "Baz::f(\"" << s << "\", id=" << id << ", obj_valid=" << obj_valid << ")\n"; }    
    
    static int ID;
    int id{-1};
    bool obj_valid{false};
};

int Baz::ID;


int main()
{
    EventManager eventMan;

    eventMan.AddClient([]{ std::cout << "lambda\n"; });
    eventMan.AddClient(std::bind([](int i){ std::cout << "lambda+bind\n"; }, 1234));
    
    std::function<void()> f1 = []{ std::cout << "std::function\n"; };
    eventMan.AddClient(f1);
    
    std::function<double(double)> f2 = [](double d){ std::cout << "std::function+bind\n"; return d;};
    eventMan.AddClient(std::bind(f2, .5));
    
    eventMan.AddClient(Foo());
    eventMan.AddClient(Bar());

    eventMan.AddClient(std::bind(&Baz::f, Baz(), "baz_t")); // ctor and move
    eventMan.AddClient(std::bind(&Baz::f, std::make_shared<Baz>(), "baz_p")); // same with shared_ptr

    {
        //Baz baz; // ctor
        //eventMan.AddClient(std::bind(&Baz::f, baz, "baz_o")); // copy ctor and move
    }
    
    {
        Baz baz;
        eventMan.AddClient(std::bind(&Baz::f, std::move(baz), "baz_m")); // invokes copy ctor
    }
    
    {
        Baz baz;
        eventMan.AddClient(std::bind(&Baz::f, std::ref(baz), "baz_r")); // no copy, goes out of scope, UB
    }
    
    eventMan.Emit();
}