如何实现一个通用按钮类,单击时执行不同的功能?

发布于 2024-11-04 18:37:50 字数 634 浏览 2 评论 0原文

好的,我正在使用 SDL 和 C++,并开始为我的地图编辑器开发一个简单的 GUI。根据单个按钮的工作原型,我编写了一个通用 GUIObject 类和一个通用 Button 类。但我现在有一个问题,为了使该类可重用,我需要能够为每个按钮设置它调用的函数(即 Help! 打开帮助窗口,Open 打开文件对话框,它们是相同的Button类,但执行不同的功能)。

现在,我前段时间使用了一个简单的 Ruby GUI 库,它的语法使用了一些被标记为“块运算符”的东西:

示例:

@login_button = Button.new(@window, 98, 131, Network_Data::LOGIN_BUTTON){execute_login}

最后一部分 {} 是您以某种方式放置自己的函数和类的地方提取该函数并在单击时执行该函数。我还听说 Java 有类似的东西:

onClick = new Function() { }

那么我将如何在 C++ 程序(使用 SDL 库)中实现它。我不想使用任何 GUI 库,因为它们看起来太复杂,而且我很难将这些库插入到我已经构建的代码中。

Okay, I'm using SDL with C++ and I started working on a simple GUI for my Map Editor. From a working prototype of a single button, I wrote a generic GUIObject class and a generic Button class. But I have a problem now, In order for the class to be reusable, I need to be able to set for each button what function it calls (i.e. Help! opens the help window, Open opens the file dialog box, they are the same Button class, but they execute different functions).

Now, I used some time ago a simple library for Ruby GUI, and it's syntax used something that was notated there as a "block operator":

Example:

@login_button = Button.new(@window, 98, 131, Network_Data::LOGIN_BUTTON){execute_login}

The last part {} was the thing where you put your own function and the class somehow extracted that and executed that function when clicked. I also heard Java has something similar:

onClick = new Function() { }

So how would I go and implement this in a C++ program (which uses SDL libraries). I don't want to use any GUI libraries because they seem too complicated and I would have a hard time inserting those libraries in my already built code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

蓝天白云 2024-11-11 18:37:50

您可以使用函数/函子/方法指针来完成此操作。 (函数应该足够并且相对简单)

您可以在按钮构造函数中传递指向回调函数的指针。

这是使用函数指针的基本示例。

class Button
{   
    typedef void(*eventFunction)();

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() () const
    {
        (m_funcPtr)();
    }
    ///< Invokes the registered function.

    private:
        eventFunction m_funcPtr;
};

您可以这样使用它:

void myFunction1() { /* do something */ }
void myFunction2() { /* do something else */ }

Button myButton1( &myFunction1 );
myButton1(); // invoke the function registered

Button myButton2( &myFunction2 );
myButton2(); // invoke the function registered

这是一个使用可变参数模板 (C++0x) 的更复杂的示例

template < class Tr, class... Args >
class Button
{
    typedef Tr(*eventFunction)(Args...);

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() ( Args&... args ) const
    {
        (m_funcPtr)(args...);
    }
    ///< Invokes the registered function with the provided arguments.
    /// @param args The arguments to transmit to the invoked function.

    private:
        eventFunction m_funcPtr;
};

您可以这样使用它:

void myFunction1( int, double ) { /* do something */ }
void myFunction2() { /* do something */ }

Button<void, int, double> myButton1( &myFunction1 );
myButton1( 10, 20.5 );  // invoke the function registered with two arguments

Button<void> myButton2( &myFunction2 );
myButton1(); // invoke the function registered without argument

Tr 模板可以删除并替换为 void 如果您从不打算使用返回 void 的函数以外的其他函数。

因此ButtonmyButton1( &myFunction1 ); 将更改为 ButtonmyButton1( &myFunction1 );

Button; myButton2( &myFunction2 ); 将更改为 Button<>; myButton2( &myFunction2 );


您可以使用方法(您需要注册一个指向要调用的类实例的指针以及指向该方法的指针)和函子(您需要注册指向要调用的函子实例的指针)。


根据您的要求,这里是使用方法执行此操作的同一个类。您需要找到一种方法让您的 Button 类立即接受函数、函子和方法(想想 virtual ^^)。这是 C++0x 版本

template < class C, class Tr, class... Args >
class Button
{
    typedef Tr(C::*eventMethod)(Args...);
    ~Button(){}

    Button( C& target, eventMethod method ) : m_target(target), m_method(method) {}
    ///< A constructor.
    /// @param target The class member instance used to call the method.
    /// @param method The class member method pointer to register.

    bool operator () ( Args&... args ) const
    {
        (m_target.*(m_method))(args...);
    }
    ///< Invokes the registered method with the provided arguments.
    /// @param args The arguments to transmit to the invoked method.

    private:
        eventMethod m_method;
        C& m_target;
};

,您可以这样使用它:

class MyClass
{
    void method1(){};
    void method2( int, double ){};
};

MyClass myC1;

Button<MyClass, void> myButton1( myC1, &MyClass::method1 );
myButton1(); // invoke the registered method using the registered class instance.

Button<MyClass, void, int, double> myButton2( myC1, &MyClass::method2 );
myButton2( 15, 170.5 ); // invoke the registered method using the registered class instance.

如果您需要从该方法所属的类内部调用它,请使用 this 作为目标参数,而不是 myC1< /代码>。

You can do it using function/functor/method pointers. (functions should be enough and relatively simple)

you can pass a pointer to the callback function in you button constructor.

Here is a basic example using function pointers.

class Button
{   
    typedef void(*eventFunction)();

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() () const
    {
        (m_funcPtr)();
    }
    ///< Invokes the registered function.

    private:
        eventFunction m_funcPtr;
};

And you can use it this way:

void myFunction1() { /* do something */ }
void myFunction2() { /* do something else */ }

Button myButton1( &myFunction1 );
myButton1(); // invoke the function registered

Button myButton2( &myFunction2 );
myButton2(); // invoke the function registered

Here is a more complex example using variadic templates (C++0x)

template < class Tr, class... Args >
class Button
{
    typedef Tr(*eventFunction)(Args...);

    Button( eventFunction funcPtr ) : m_funcPtr( funcPtr ){}
    ///< A constructor.
    /// @param funcPtr The function pointer to register.

    ~Button(){ m_funcPtr = NULL; }
    ///< A destructor.          

    void operator() ( Args&... args ) const
    {
        (m_funcPtr)(args...);
    }
    ///< Invokes the registered function with the provided arguments.
    /// @param args The arguments to transmit to the invoked function.

    private:
        eventFunction m_funcPtr;
};

And you can use it this way:

void myFunction1( int, double ) { /* do something */ }
void myFunction2() { /* do something */ }

Button<void, int, double> myButton1( &myFunction1 );
myButton1( 10, 20.5 );  // invoke the function registered with two arguments

Button<void> myButton2( &myFunction2 );
myButton1(); // invoke the function registered without argument

The Tr template can be removed and replaced by void if you never plan to use others functions than functions returning void.

Thus Button<void, int, double> myButton1( &myFunction1 ); would change to Button<int, double> myButton1( &myFunction1 );

and Button<void> myButton2( &myFunction2 ); would change to Button<> myButton2( &myFunction2 );


You can do almost the same thing with methods (you need to register a pointer to the instance of the class to call AND the pointer to the method) and with functors (you need to register a pointer to the instance of the functor to call).


On your demand here is the same class to do it with methods. It's up to you to find a way to make your Button class accept functions, functors and methods at once (think virtual ^^). This is the C++0x version

template < class C, class Tr, class... Args >
class Button
{
    typedef Tr(C::*eventMethod)(Args...);
    ~Button(){}

    Button( C& target, eventMethod method ) : m_target(target), m_method(method) {}
    ///< A constructor.
    /// @param target The class member instance used to call the method.
    /// @param method The class member method pointer to register.

    bool operator () ( Args&... args ) const
    {
        (m_target.*(m_method))(args...);
    }
    ///< Invokes the registered method with the provided arguments.
    /// @param args The arguments to transmit to the invoked method.

    private:
        eventMethod m_method;
        C& m_target;
};

And you can use it this way:

class MyClass
{
    void method1(){};
    void method2( int, double ){};
};

MyClass myC1;

Button<MyClass, void> myButton1( myC1, &MyClass::method1 );
myButton1(); // invoke the registered method using the registered class instance.

Button<MyClass, void, int, double> myButton2( myC1, &MyClass::method2 );
myButton2( 15, 170.5 ); // invoke the registered method using the registered class instance.

If you need to call it from inside the class the method belong to, use this as target parameter instead of myC1.

︶ ̄淡然 2024-11-11 18:37:50

有两种方法。您可以使用运行时多态性(boost::functionstd::tr1::functionstd::function),或者您可以使用模板。

There are two approaches. You can use run-time polymorphism (boost::function or std::tr1::function or std::function) or you can use a template.

塔塔猫 2024-11-11 18:37:50

您将需要按钮类来调用用户指定的函数。我建议您查看 Boost Signals 库。该库实现了信号和槽方法,有非常好的文档并且得到了很好的支持。此外,这些链接对于了解总体思路应该很有用:

希望有帮助。祝你好运!

You will need your button class to call functions specified by its users. I recommend you to take a look at Boost Signals library. That library implements signals and slots approach, has very nice documentation and is very well supported. Also, these links should be useful to get the general idea:

Hope it helps. Good luck!

拒绝两难 2024-11-11 18:37:50

它在 C++ 中的工作方式与在 Java 中使用的方式几乎相同:

onClick = new ClickFunction;

但是显然您需要以下内容来定义必要的类:

template<class A, class B>
class Function { public: virtual B Map(A a) const=0; };

Function<int, int> *onClick;

class ClickFunction : public Function<int,int>
{
public:
    ClickFunction(MyObject &o) : o(o) { }
    int Map(int a) const { return 10; }
private:
    MyObject &o;
};

It works in almost the same way in c++ as you'd use in java:

onClick = new ClickFunction;

but then you'd obviously need the following to define necessary classes:

template<class A, class B>
class Function { public: virtual B Map(A a) const=0; };

Function<int, int> *onClick;

class ClickFunction : public Function<int,int>
{
public:
    ClickFunction(MyObject &o) : o(o) { }
    int Map(int a) const { return 10; }
private:
    MyObject &o;
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文