简体   繁体   English

C ++使用成员函数回调实现按钮

[英]C++ Implementing a Button with member-function callback

I am trying to implement a simple GUI component in C++. 我正在尝试在C ++中实现一个简单的GUI组件。 The 'Button' class exists in a library that is completely de-coupled from the main program. “ Button”类存在于与主程序完全分离的库中。 I want to be able to pass a function-pointer to a button that can be ran upon clicking. 我希望能够将功能指针传递给单击后即可运行的按钮。

I had some success until I moved the 'button' from being a struct to class for readability and expandability. 在将“按钮”从结构变为类的可读性和可扩展性之前,我取得了一些成功。 I reckon that it only worked by pure chance as now I get very random problems. 我认为这只是偶然的机会,因为现在我遇到了非常随机的问题。

I basically have something like this: 我基本上是这样的:

   typedef void(BaseMenu::*ClickAreaCallback)(Button*);
   struct Message{
            ClickAreaCallback func;
            Button* clickArea;
            BaseMenu* funObj;
        };

The from my classes that subclass BaseMenu, I do something like this: 从我的类继承BaseMenu,我做这样的事情:

   cb = (ButtonContainer::ClickAreaCallback)&TileSelectorScreen::setTileMode;

and set: 并设置:

   ClickAreaCallback to &cb (as well as funObj)

I then run it upon 'click' by doing: 然后,通过执行以下操作,在“点击”时运行它:

m->funObj->*m->func)(m->clickArea);

This is obviously wrong as I've read there are problems passing non-static member functions and expecting them to run. 这显然是错误的,因为我已经阅读过传递非静态成员函数并期望它们运行的​​问题。

So, is what I am doing impossible? 那么,我在做什么是不可能的吗? Is what I want possible by using plain C++ without boost or using -std=c++11. 我想通过不使用boost的普通C ++或使用-std = c ++ 11来实现。 I'm limiting myself to the very basics so I can still compile for many platforms. 我将自己限制在最基本的方面,因此我仍然可以针对许多平台进行编译。

In short: I want a simple method of calling functions from a class that knows nothing of the class it's calling. 简而言之:我想要一个简单的方法来从类中调用函数,而该类对所调用的类一无所知。

thanks in advance. 提前致谢。

In principle there is nothing wrong with pointers to members. 原则上,指向成员的指针没有错。 See, eg, the following code: 参见例如以下代码:

#include <iostream>

/** Some API */
struct Button {
    virtual void OnClick() = 0;
};

struct BaseMenu {

    void f1(Button* b)  {
        std::cout << "f1(Button*)\n";
        b->OnClick();
    }

    void f2(Button* b)  {
        std::cout << "f2(Button*)\n";
        b->OnClick();
    }

    void Update() {     
    }
};

typedef void(BaseMenu::*ClickAreaCallback)(Button*);

struct Message{
    ClickAreaCallback func;
    Button* clickArea;
    BaseMenu* funObj;
};

/** Usage */
class OKButton : public Button {
    void OnClick() {
        std::cout << "OKButton::OnClick()\n";
    }
};

int main(int nArg, char* args[]) {
    // Fill message:
    BaseMenu menu;
    OKButton ok;
    Message m1, m2;
    m1.func = &BaseMenu::f1;
    m1.funObj = &menu;
    m1.clickArea = dynamic_cast<Button*>(&ok);

    m2.func = &BaseMenu::f2;
    m2.funObj = &menu;
    m2.clickArea = dynamic_cast<Button*>(&ok);

    (m1.funObj ->* m1.func)(m1.clickArea);
    (m2.funObj ->* m2.func)(m2.clickArea);
}

But it looks like a conceptional error. 但这似乎是一个概念上的错误。 You should not need the callback. 您不需要回调。 The buttons should be derived from a base class and have virtual member functions that do the specific stuff. 这些按钮应该派生自基类,并具有执行特定任务的虚拟成员函数。

There follows an example demonstrating the usage of inheritance instead of callbacks. 下面的示例演示继承的用法,而不是回调。 Note, that ButtonToggle is an example for storing the information inside the button and ButtonNotify is an example for the button notifying the menu. 请注意,ButtonToggle是用于在按钮内部存储信息的示例,而ButtonNotify是用于通知菜单的按钮的示例。

#include <iostream>
#include <vector>

/** Some API */
struct Button {
    double _area[4]; // rectangle x1,y1,x2,y2
    Button(std::initializer_list<double> area) {
        std::copy(area.begin(),area.begin()+4,_area);
    }
    virtual void OnClick() = 0;
};

class BaseMenu {
protected:
    std::vector<Button*> _buttons;
public:
    void Register(Button* btn) {
        _buttons.push_back(btn);
    }
    void OnClick(double pt[2]) {
        for(auto iBtn = _buttons.begin(); iBtn!=_buttons.end(); iBtn++) {
            if( (*iBtn)->_area[0] <= pt[0] && pt[0] <= (*iBtn)->_area[2]
                && (*iBtn)->_area[1] <= pt[1] && pt[1] <= (*iBtn)->_area[3] ) {
                (*iBtn)->OnClick();
            }
        }
    }
};

struct MyMenu : public BaseMenu {
    struct ButtonToggle: public Button {
        bool _val;
        ButtonToggle() :
            Button( {0.0,0.0,1.0,1.0} )
        {
            _val = false;
        }
        void OnClick()
        {
            std::cout << "ButtonToggle::OnClick()\n";
            _val = not(_val);
        }
    } buttonToggle;

    void DoSomething() {
        std::cout << "DoSomething()\n";
    }

    struct ButtonNotify: public Button {
        MyMenu& _myMenu;
        ButtonNotify(MyMenu& myMenu) :
            Button( {2.0,0.0,3.0,1.0} ),
            _myMenu(myMenu)
        {}

        void OnClick() {
            _myMenu.DoSomething();
        }
    } buttonNotify;

    MyMenu() :
        buttonNotify(*this)
    {
        Register(&buttonToggle);
        Register(&buttonNotify);
    }
};

int main(int nArg, char* args[]) {
    MyMenu menu;
    double pt[2];

    while(( std::cout << "\nCoordinates (end: -1 -1):",
            std::cin >> pt[0] >> pt[1],
            not( pt[0] == -1.0 and pt[1] == -1.0 ) )) {
        menu.OnClick(pt);
    }
}

/*
    Local Variables:
    compile-command: "g++ -g -std=c++11 test1.cc"
    End:
 */

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM