Reputation: 131
Is there a way to use a function pointer for both functions and member functions?
I want to create a kind of event-handle which can hold member functions of a class and at the same time it should be capable to have "normal" function pointers.
EDIT: I have found a solution. Probably not the best one but one;
Event.h
#include <map>
class BasicEvent {
public:
BasicEvent(int id) : eventId(id)
{}
int GetId() const
{
return eventId;
}
virtual void ObjectMethode() {}
protected:
const int eventId;
};
class BasicEventHandler {
public:
void Trigger(BasicEvent* event)
{
if (this->eventMap.find(event->GetId()) != this->eventMap.end()) {
eventMap[event->GetId()](event);
}
}
void AddEvent(int id, std::function<int(BasicEvent*)> eventFunction)
{
eventMap[id] = eventFunction;
}
void RemoveEvent(int id)
{
this->eventMap.erase(id);
}
private:
std::map<int, std::function<int(BasicEvent*)>> eventMap;
};
main.cpp
class CustomEvent : public BasicEvent {
public:
CustomEvent(int id) : tq::BasicEvent(id)
{
x = 9;
y = 8;
}
struct {
int x;
int y;
};
void ObjectMethode()
{
std::cout << this->eventId << " Custom Event: X: " << x << " Y: " << y << '\n';
}
};
int main() {
BasicEventHandler myHandler;
myHandler.AddEvent(0, [&](BasicEvent* event) {
std::cout << " EVENT <" << event->GetId() << "> Triggered!\n";
return 0;
});
myHandler.AddEvent(1, [&](tq::BasicEvent* event) {
event->ObjectMethode();
return 0;
});
myHandler.Trigger(&BasicEvent(0));
myHandler.Trigger(&CustomEvent(1));
return 0;
}
Upvotes: 1
Views: 223
Reputation: 870
C++11 and Lambdas .. yay!!
So here we go..
Let us have a function, your event handler which will take a predicate(a function that returns true or false) and two functions namely on_sucess
and on_error
.
If the test_predicate
returns true
then on_success
shall be invoked, else on_error
would be invoked.
template<typename Predicate, typename OnSucess, typename OnError>
void single_event_handler (Predicate && test_predicate,
OnSucess && on_success,
OnError && on_error)
{
if( test_predicate() )
on_success();
else
on_error();
}
And now our caller side. Here we have 3 functions that we want to fit into this pattern.
bool is_even(int num) { return !(num % 2); }
void on_even_found() { std::cout << "Number passed is even\n"; }
void on_odd_found() { std::cout << "Number passed is odd\n"; };
But as you can see, the is_even
function does not fit into the signature of test_predicate
. is_even(int)
takes a single argument, while test_predicate()
takes none.
So how do we fix this ?
We wrap it into one more function and/or lambda, and then pass it.
Guiding Principle : Every problem can be solved by adding one more layer.
// wrap it in another function or lambda
auto is_5_even = []() { return is_even(5); };
auto is_4_even = []() { return is_even(4); };
And now once all the pieces of our puzzle is ready, we create our own event handler.
// caller side - pair the final structure
single_event_handler(is_5_even, on_even_found, on_odd_found);
single_event_handler(is_4_even, on_even_found, on_odd_found);
Now this code can be implemented in pre C++11, by using bind and function pointer.
Sadly std::bind
and std::function
were introduced in C++11 and you would have to use other alternatives such as boost library.
Moreover, another wrapper can be written on top of single_event_handler
which can manage multiple events at once, but I think we will have to create another function register_event
so that it can be used effectively.
Here is the full working code, for your reference (compiled using g++ -std=c++11
)
#include <iostream>
template<typename Predicate, typename OnSucess, typename OnError>
void single_event_handler (Predicate && test_predicate,
OnSucess && on_success,
OnError && on_error)
{
if( test_predicate() )
on_success();
else
on_error();
}
bool is_even(int num) { return !(num % 2); }
void on_even_found() { std::cout << "Number passed is even\n"; }
void on_odd_found() { std::cout << "Number passed is odd\n"; };
int main()
{
// caller side
auto is_5_even = []() { return is_even(5); };
auto is_4_even = []() { return is_even(4); };
single_event_handler(is_5_even, on_even_found, on_odd_found);
single_event_handler(is_4_even, on_even_found, on_odd_found);
return 0;
}
Upvotes: 2
Reputation: 503
where will the member function get its "this" pointer? the two functions cannot have the same signature. Probably, your best bet will be to create a lambda, that will capture the "this", and send it to your event-handler.
std::function
will be able to hold it for you.
Upvotes: 1
Reputation: 170074
Accept your handler as a std::function
, and clients can then pass in whatever they want. Be it functions, lambdas, other functors, you name it.
The template will handle the details for you, and the clients can choose how they wish to be called.
Upvotes: 1