2013-10-04 2 views
0

C++에서 간단한 GUI 구성 요소를 구현하려고합니다. 'Button'클래스는 주 프로그램에서 완전히 분리 된 라이브러리에 있습니다. 나는 클릭시 실행할 수있는 버튼에 함수 포인터를 전달할 수 있기를 원합니다.C++ 멤버 함수 콜백 버튼 구현

가독성과 확장 성을 위해 구조체에서 '버튼'을 옮길 때까지 약간의 성공을 거두었습니다. 나는 매우 무작위적인 문제를 겪었으므로 그것은 단지 순수한 기회에 의해서만 작용했다고 생각한다. BaseMenu를 서브 클래스 내 수업에서

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

(가), 나 같은 것을 할 :

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

및 설정 :

ClickAreaCallback to &cb (as well as funObj) 

I

나는 기본적으로 이런 일이 다음을 수행하여 '클릭'시 실행하십시오 :

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

정적 인 멤버 함수를 전달하고 실행하려고 할 때 문제가 있다는 것은 분명히 잘못되었습니다.

그래서 내가 할 수없는 일이 무엇입니까? 부스트를 사용하지 않거나 -std = C++ 11을 사용하지 않고 일반 C++을 사용하여 원하는 것이 무엇입니까? 나는 많은 기초를 가지고 여전히 컴파일 할 수 있도록 자신을 제한적으로 제한하고있다.

간단히 말해서 나는 호출하는 클래스를 모르는 클래스에서 함수를 호출하는 간단한 방법을 원합니다. 사전에

감사합니다.

+0

캐스팅 함수 포인터는 결코 어떤 문제에 대한 해결책이 아닙니다. 하지 마. – molbdnilo

+0

나는 이것을 알고 올바른 이유가 무엇인지 묻고있다. 의견 있으십니까? – user2452192

답변

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: 
*/ 
+0

하지만 분명히 버튼은 그들이하는 일의 구체적인 내용을 알지 못합니다. 그들은 벙어리가되어야하고 단순히 "나는 클릭했습니다"라고 말합니다. 아마 C++이 의도하지 않은 것을하려고하지만 믿기가 어렵다는 것을 알게됩니다. 코드 주셔서 감사합니다, 그것은 거의 내가 뭘했는지, 난 그냥 별도의 클래스로 옮길 때 문제가있었습니다. – user2452192

+0

당신은 다음과 같이 썼습니다. "하지만 분명히 버튼은 그들이하는 일의 구체적인 내용을 알지 못합니다." 나는 그렇게 확신하지 않는다. 기본 클래스 Button 및 BaseMenu에 대해서도 마찬가지입니다. 그러나 더 많은 정보 및/또는 조치가 포함 된 특정 Button을 파생시킬 수 있습니다. 위의 확장 된 답변을 참조하십시오. – Tobias