Generic member function pointer help

2019-08-16 16:15发布

Hey, I've got a question about general member function pointers. I'm trying to achieve something similar to the following question How to define a general member function pointer

Essentially what I want to be able to do is to register a member function pointer that accepts a generic Event object as its argument with a specific event type (for example, a KeyboardEvent). Then, in my input management class, what I want to be able to do is whenever the user hits a key, I can create a KeyboardEvent object that contains some data about the keypress, and call all of those member function pointers that I registered with the KeyboardEvent type with my KeyboardEvent object as their parameter.

I've been playing around with boost bind and boost function and they seem to enable me to do 95% of what I want to do, the only problem I am trying to store all of the member function pointers in a vector, but they're all of different types so I can't do that. So I bind them into a function object when I first register the method with my event handling system, which expects me to specify all of the arguments at this point in the execution. I don't have to objects at this point though as the objects are generated by some unknown event in the future.

Sorry if none of this makes any sense.

3条回答
戒情不戒烟
2楼-- · 2019-08-16 16:54

This is not really an answer to your problem, but merely a hint that what you're trying to accomplish already exists.

It seems like you're trying to implement the Observer pattern. You can read about it here. Once you've grasped the Observer pattern, check out the Boost.Signals2 library. A boost::signal is like a vector of event handler function pointers, but much more powerful.

Here is an example of using Boost::Signals2 for detecting inputted characters. Hope it helps.

#include <iostream>
#include <boost/signals2.hpp>
#include <boost/bind.hpp>

//-----------------------------------------------------------------------------
struct KeyboardEvent
{
    char key;
};

//-----------------------------------------------------------------------------
class Keyboard
{
private:
    typedef boost::signals2::signal<void (KeyboardEvent)> SignalType;
    SignalType signal_;

public:
    typedef SignalType::slot_type SlotType;

    // Subscribes to keyboard events
    boost::signals2::connection connect(const SlotType& slot)
    {
        return signal_.connect(slot);
    }

    // Scans for keyboard events and notifies subscribers
    void scan()
    {
        KeyboardEvent event;
        std::cin >> event.key;
        signal_(event);
    }
};

//-----------------------------------------------------------------------------
class KeyPrinter
{
private:
    void onKey(KeyboardEvent event)
    {
        std::cout << "You hit \'" << event.key << "\'\n";
    }

    boost::signals2::connection connection_;

public:
    void connect(Keyboard& keyb)
    {
        connection_ = keyb.connect(
            boost::bind(&KeyPrinter::onKey, this, _1) );
    }
    void disconnect() {connection_.disconnect();}
};

//-----------------------------------------------------------------------------
class Quitter
{
private:
    void onKey(KeyboardEvent event)
    {
        if (event.key == 'q' || event.key == 'Q') {quit_ = true;}
    }

    boost::signals2::connection connection_;
    bool quit_;

public:
    void connect(Keyboard& keyb)
    {
        quit_ = false;
        connection_ = keyb.connect(
            boost::bind(&Quitter::onKey, this, _1) );
    }
    void disconnect() {connection_.disconnect();}
    bool quitDetected() {return quit_;}
};

//-----------------------------------------------------------------------------
int main()
{
    Keyboard keyb;
    KeyPrinter printer;
    Quitter quitter;

    printer.connect(keyb);
    quitter.connect(keyb);

    while (!quitter.quitDetected())
    {
        keyb.scan();
    }
}
查看更多
祖国的老花朵
3楼-- · 2019-08-16 16:59

(Here's a more direct answer to your boost::function/bind problem, without all the Boost.Signals2 stuff.)

It seems you're not using the _1, _2, etc. placeholders when using boost::bind. This example illustrates how you should use them:

struct KeyboardEvent { char key; };

typedef boost::function<void (KeyboardEvent)> KeyboardHandler;

struct Handler
{
    void onKey(KeyboardEvent event) {std::cout << event.key;}
};

int main()
{
    Handler handler1, handler2;
    std::vector<KeyboardHandler> handlers;
    handlers.push_back(boost::bind(&Handler::onKey, &handler1, _1));
    handlers.push_back(boost::bind(&Handler::onKey, &handler2, _1));
    KeyboardEvent event;
    event.key = 'z';
    handlers[0](event);
    handlers[1](event);
}
查看更多
萌系小妹纸
4楼-- · 2019-08-16 17:09

if I understand your question correctly, you can try following

// function pointer, function with similar signature can be cast as function_t
typedef int (*function_t)(object_t *event, ...);
std:: vector<function_t> function_pointers;

// populate vector, e.g. function_pointers.push_back((function_t)&some_function)

void handle_event(object_t *event) {
  for (...) (function_pointers.at(i))(event, ...);
}

I reread your question, and finally caught member function part. for this you can use functor.

struct base {
  virtual void function(event_t *event)= 0;
};

template<class A>
struct F : base { 
  F(A &a) : object_(a) {}
  virtual void function(event_t *a) { object_.function(a); }
  A &object_;
};

std:: vector<base*> callback_vector;
...
callback_vector.push_back(new F<event_handler>(event_handler_object));
查看更多
登录 后发表回答