Reputation: 6428
What's the best way to connect a GTK+ signal to a non-static member function?
I have a GUI class in c++ that uses gtk, and i want do something like this:
Gui::Gui ()
{
gtk_signal_connect(GTK_OBJECT(somObject),
"clicked",
GTK_SIGNAL_FUNC( &(this->ClickHandler) ),
NULL);
}
void Gui::ClickHandler(GtkWidget *w, gpointer data)
{
// handle the click
}
I know this doesn't work b/c the GTK_SIGNAL
_FUNC can't point to a member function unless it's a static function, so what's the best way to do this? Is it possible to use a single proxy handler function and boost::bind somehow? Here is what I tried:
gtk_signal_connect(GTK_OBJECT(somObject), "clicked",
GTK_SIGNAL_FUNC( SigHandler ),
boost::bind( &(Gui::ClickHandler), this) );
void SigHandler(GtkWidget *w, gpointer data)
{
data(w);
}
void Gui::ClickHandler(GtkWidget *w) { // handle the click }
here's the error: gui.cc: error: 'data' cannot be used as a function
Upvotes: 1
Views: 3392
Reputation: 5476
I assume your class contains some internal data that the signal handler needs access to. If not, just use a static function for the signal handler. Otherwise, here are a couple of ideas:
Create a sigc++ or boost functor out of the member function; set it as the data passed to the signal handler. Use a generic signal handler that just calls the functor.
During construction, register each instance of the class somewhere and use a signal handler that calls the member function for each registered instance.
Upvotes: 2
Reputation: 101555
You will need the proxies, but you can do some template magic to help you generate them. Something like this:
template <class HandlerClass, class A1, void(HandlerClass::*HandlerFunc)(A1)>
struct GtkHandlerProxy {
static void HandlerProxy(A1 a1, gpointer p) {
(static_cast<HandlerClass*>(p)->*HandlerFunc)(a1);
}
};
and then use it:
Gui::Gui()
{
gtk_signal_connect(
GTK_OBJECT(somObject),
"clicked",
GTK_SIGNAL_FUNC(&GtkHandlerProxy<Gui, GtkWidget*, &Gui::ClickHandler>
::HandlerProxy),
this);
}
You'd probably want to define a helper macro, too:
#define GTK_SIGNAL_METHOD(Class, Handler, A1) \
GTK_SIGNAL_FUNC(GtkHandlerProxy<Class, A1, &Class::Handler>::HandlerProxy)
so that you can write:
Gui::Gui()
{
gtk_signal_connect(
GTK_OBJECT(somObject),
"clicked",
GTK_SIGNAL_METHOD(GtkHandlerProxy, ClickHandler, GtkWidget*),
this);
}
This only covers the single-argument handler case; to handle something like button_press_event
, you'll have to define similar helpers for 2+ arguments. Boost preprocessor library can help avoid repetitive code there.
[EDIT]
Alternatively, you could pass a function object itself as p
, which can possibly include captured variables (e.g. boost::bind
or boost::lambda
). Here's how that would look:
class Gui {
...
// Member to ensure proper lifetime
boost::function<void(GtkWidget*)> clickHandlerFunc;
Gui() {
...
clickHandlerFunc = boost::bind(&Gui::ClickHandler, this);
gtk_signal_connect(
GTK_OBJECT(somObject),
"clicked",
GTK_SIGNAL_FUNC(UniversalHandlerProxy<GtkWidget*>)
&clickHandlerFunc);
}
template <class A1>
static void Gui::UniversalHandlerProxy(A1 a1, gpointer p) {
(*static_cast< boost::function<void(A1)>* >(p))(a1);
}
void Gui::ClickHandler(GtkWidget* w, gpointer p) {
...
}
};
Upvotes: 2
Reputation: 200746
Use a static proxy:
Gui::Gui ()
{
gtk_signal_connect(GTK_OBJECT(somObject),
"clicked",
GTK_SIGNAL_FUNC(ClickHandlerProxy),
this);
}
void Gui::ClickHandler(GtkWidget *w)
{ /* handle the click */ }
void Gui::ClickHandlerProxy(GtkWidget *w, gpointer p)
{ static_cast<Gui*>(p)->ClickHandler(w); }
Upvotes: 1