没有继承的C++函数指针(回调)

C++ Function Pointer (Callback) without Inheritance

本文关键字:指针 回调 函数 C++ 继承      更新时间:2023-10-16

我正在一个输入事件系统中工作,在该系统中,当特定输入发生而不进行轮询时,我可以非常灵活地使用该做什么。我正在使用SDL收集输入,因此您将看到一些SDL事件。

我计划通过将一个事件(Keypress)"绑定"到一个函数来完成这一切。我希望这适用于任何具有InputManager实例的类。

这就是我现在为InputManger类所做的:

//InputManager.h
typedef void (*Callback)();
typedef std::unordered_map<SDL_Keycode, Callback> KeyCode;
typedef std::unordered_map<int, KeyCode> Binding;
class InputManager {
public:
    void bindInput(EventType eventType, SDL_Keycode key, Callback f);
    void updateInput(SDL_Event event);
private:
    Binding inputBindings;
};
...
//InputManager.cpp
void InputManager::bindInput(SDL_EventType eventType, SDL_Keycode key, Callback f)
{
    inputBindings[eventType][key] = f;
}
void InputManager::updateInput(SDL_Event event)
{
    while(SDL_PollEvent(&event)) {
        if (inputBindings[event.type][event.key.keysym.sym])
            //Exists call function here
    }
}

这就是将密钥绑定到函数的示例:

//A.cpp
//This function will be called when the class is being initialized
void init(InputManager* m_InputManager) {
    m_InputManager->bindInput(SDL_KEYDOWN, keypress_A, &A::testFunction);
}
//This is the callback function when A is pressed
void testFunction() {
    //Do stuff
}

我的问题是它不需要任何成员(A)。实际错误代码为Cannot initialize a parameter of type 'Callback' (aka 'void (*)()') with an rvalue of type 'void (A::*)()'

现在,我将其解释为必须指定(在InputManager中)才能执行(A::*)。问题是,我希望任何给定InputManager的类都能够使用它,而InputManager不应该访问任何其他类(本例中为A)。

我尝试过的:

我首先想到的是模板。我对他们没有那么多经验,所以他们仍然是答案,但我对他们失败了。由于回调函数是在typedef中定义的,所以我无法直接应用该模板。这就是它最终的样子:

template <class T>
struct Callback {
    typedef void (T::*F)();
};
template <class T>
struct KeyCodeStruct {
    typedef std::unordered_map<SDL_Keycode, Callback<T>> KeyCode;
};
template <class T>
struct BindingStruct {
    typedef std::unordered_map<int, KeyCodeStruct<T>> Binding;
};
class InputManager {
public:
    template <class T>
    void bindInput(SDL_EventType eventType, SDL_Keycode key, Callback<T> f);
    void updateInput(SDL_Event event);
private:
    template <class T>
    BindingStruct<T> inputBindings; //This is the area where I loose my grip on a template.
    //^^^ Member 'inputBindings' declared as a template
};

在这个错误之后,我基本上把我可能不应该做的事情放进了模板中,并做了一些我根本不理解的事情。我想还是问问比较好。我读了很多关于函数指针的东西,也许答案很简单,但我似乎无法理解,所以也许可以试着用莱曼的术语为我定义它?

编辑:

此外,我还应该补充一点,我计划在整个项目中分发我的InputManager,我之所以在标题中不使用继承,是因为我读到了一个解决方案,其中您有一个基类,所有其他文件都来自该基类。我真的不能在一个大型项目上这样做。如果这是唯一的方法,我最终可能会这么做,但届时我可能会坚持投票。

问题:

问题是Callback被定义为指向返回void的函数的指针,而您的绑定尝试使用的是指向a类成员函数的指针&A::testFunction

指向函数的指针可以在无需进一步要求的情况下调用。但是,指向类的成员函数的指针需要为正确类的对象x(或指向对象的指针)调用:

(x.*f)();    // call member function with object of the right member class

模板方法的问题是,您需要为绑定存储,不仅要存储指向成员函数的指针,还要存储指向T类型对象的指针,这样您就可以为对象调用该函数。

解决方案

有一个使用<functional>的优雅解决方案,它允许存储函数和绑定。这也适用于成员函数和合适类的对象之间的绑定。

这里有一个概念的小演示,由您根据需要进行调整:

class Test {     // function with a callback for demo of proof of concept 
public:
    void myfct() {
        cout << "Myfct !!n";
    }
}; 
class Manager {    // Simplified manager
    std::function <void()> f;    // this is the trick !! 
public: 
    template <class T>
    void binding(T *object, void (T::*mf)()) {
        f = std::bind(mf, object);   // you bind the member function to the object it has to be used with 
    }
    void testcall() {     // Just for showing the proof of concept 
        f(); 
    }
};

然后你可以用试试这个

Manager man; 
Test tst; 
man.binding(&tst, &Test::myfct); 
man.testcall();