aaronstacy
aaronstacy

Reputation: 6428

use gtk_signal_connect with a member function in c++

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

Answers (3)

Steve S
Steve S

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

Pavel Minaev
Pavel Minaev

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

John Millikin
John Millikin

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

Related Questions