Alphastrick
Alphastrick

Reputation: 131

C++ function pointer and member function pointer

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

Answers (3)

badola
badola

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

yosim
yosim

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

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

Related Questions