event class and dynamic bind paramaters

I have this c++ class here which can register class function into event class and then call them all with 1 function later.

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
template < typename... Args>
class Event {
public:
    template <typename T>
    using ClassFunction = void (T::*)(Args...);


    // std::placeholders::_1,
    template <typename T, typename ...Types>
    void register_event(T* obj, ClassFunction<T> func, Types... typelist) {
        auto bound_func = std::bind(func, obj, typelist...);
        event_list.push_back(bound_func);
    }

    void call(Args... args) {
        for (auto& func : event_list) {
            func(args...);
        }
    }

private:
    std::vector<std::function<void(Args...)>> event_list;
};


and how to use:
1
2
3
4
5
6
7
8
9
10
11
12
class Listener {
public:
    void on_event(int x) {
        std::cout << "Event received with value: " << x << std::endl;
    }
};

    Event<int> event;
    Listener listener;

    event.register_event(&listener, &Listener::on_event, std::placeholders::_1);
    event.call(42);


Works with any number of params.
What bothers me is that register_event function needs to take in those placeholders.

There must be a better way, they should either be defined on somewhere here:
Event<int> event;
or better yet, somehow read the number of params and auto type them in later.


I don't know what c++ version i'm having available but
I can't use std::make_integer_sequence

So solution here:
https://stackoverflow.com/questions/26129933/bind-to-function-with-an-unknown-number-of-arguments-in-c

Won't help me.
I'm no expert on std::bind but it seems like you can remove std::placeholders::_1 if you replace std::bind with std::bind_front.

https://godbolt.org/z/sP5b5joq6
bind_front is C++20 but OP only has C++11. This is known since they don't have C++14's make_integer_sequence but do have C++11's template parameter packs.

One option is something like this:
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
#include <functional>
#include <iostream>
#include <vector>

template <typename... Args>
  class Event 
  { 
    std::vector<std::function<void(Args...)>> functions;

  public:
    template <typename T>
      void register_event(T* object, void (T::*pmfn)(Args...))
      {
        functions.push_back([object, pmfn](Args... args) { 
          return (object->*pmfn)(args...); });
      }
    
    void call(Args... args) { for (auto const& f: functions) f(args...); }
    
    ~Event() = default;
    
    Event(Event const&) = delete;
    Event& operator=(Event const&) = delete;

    Event(Event&&) noexcept = default;
    Event& operator=(Event &&) noexcept = default;
    
    Event() = default;
  };

int main()
{
    class Listener {
    public:
        void on_event(int x) {
            std::cout << "Event received with value: " << x << std::endl;
        }
    };

    Event <int> event;
    Listener listener;

    event.register_event(&listener, &Listener::on_event);
    event.call(42);
}
You're making this more complicated than necessary, IMHO. You don't need a template. Just use an Event class with a virtual execute() method. Derived classes contain whatever data they need for execute() to do its thing.
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
#include<iostream>
#include <string>
#include <list>

using std::cout;
using std::string;

class Event {
public:
    virtual ~Event() {}
    virtual void execute() = 0;
};

class EventA : public Event {
public:
    void execute() { cout << "This is an EventA\n"; }
};

class EventB : public Event {
public:
    EventB(int a1, const string &a2, double a3) :
	arg1(a1), arg2(a2), arg3(a3) {}
    
    void execute() {
	cout << "Event B with args " << arg1 << ", " << arg2
	     << ", " << arg3 << '\n';
    }
    int arg1;
    string arg2;
    double arg3;
};

class Simulator {
private:
    std::list<Event*> events;
public:
    // Pop events off the list, execute them and destroy them.
    void run() {
	while (events.size()) {
	    Event *e = events.front();
	    events.pop_front();
	    e->execute();
	    delete e;
	}
    }

    // Push an event. The simulator takes ownership.
    void pushEvent(Event *p) {
	events.push_back(p);
    }

    ~Simulator() {
	while (events.size()) {
	    delete events.front();
	    events.pop_front();
	}
    }
};


int
main()
{
    Simulator sim;
    Event *e;

    e = new EventA;
    sim.pushEvent(e);
    e = new EventB(1, "This is event b", 3.14159);
    sim.pushEvent(e);

    sim.run();
}


$ ./foo
This is an EventA
Event B with args 1, This is event b, 3.14159

Topic archived. No new replies allowed.