Reputation: 1434
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.
Upvotes: 3
Views: 6230
Reputation: 1226
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
.
Upvotes: 3
Reputation: 147036
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.
Upvotes: 1
Reputation: 288
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;
};
Upvotes: 0
Reputation:
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!
Upvotes: 0