1

我正在尝试在 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++ 或使用 -std=c++11 是我想要的吗?我将自己限制在最基本的范围内,因此我仍然可以为许多平台进行编译。

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

提前致谢。

4

1 回答 1

1

In principle there is nothing wrong with pointers to members. See, e.g., 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.

#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:
 */
于 2013-10-04T13:52:41.073 回答