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

时间:2013-10-04 09:49:30

标签: c++ callback function-pointers

我正在尝试用C ++实现一个简单的GUI组件。 “Button”类存在于与主程序完全解耦的库中。我希望能够将一个函数指针传递给一个可以在点击时运行的按钮。

我取得了一些成功,直到我将“按钮”从结构转移到类,以实现可读性和可扩展性。我认为它只能通过纯粹的机会起作用,因为现在我遇到了非常随机的问题。

我基本上有这样的事情:

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

我的类是BaseMenu的子类,我这样做:

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

并设置:

   ClickAreaCallback to &cb (as well as funObj)

然后我通过“点击”运行它:

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

这显然是错误的,因为我读过传递非静态成员函数并期望它们运行时出现问题。

那么,我做的不可能吗?我想要的是使用普通的C ++而不使用boost或使用-std = c ++ 11。我限制自己的基础知识,所以我仍然可以编译很多平台。

简而言之:我想要一个从类中调用函数的简单方法,该类对它所调用的类一无所知。

提前感谢。

1 个答案:

答案 0 :(得分:1)

原则上指向成员的指针没有错。 参见,例如,以下代码:

#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);
}

但它看起来像是一个概念上的错误。你不应该需要回调。这些按钮应该从基类派生而来,并具有执行特定内容的虚拟成员函数。

下面是一个演示继承而不是回调的示例。 请注意,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:
 */