event class and dynamic bind paramaters
May 28, 2023 at 9:42am UTC
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.
May 28, 2023 at 7:04pm UTC
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
May 28, 2023 at 11:42pm UTC
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);
}
May 30, 2023 at 3:23pm UTC
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.