0

I am trying to make a wrapper for a Gtk click signal to hook into my event system. The Gtk way to handle click events would be like this:

auto button = new Gtk::Button();
button->signal_clicked().connect(...);

So, to add it to my system I created a function called fromSignal which takes two inputs and I will bind them together. The problem that I am running into is on the callback(); line.

error: no match for call to '(std::_Bind<Glib::SignalProxy<void()> (Gtk::Button::*(Gtk::Widget*))()>) ()'
[build]    18 |   callback();
[build]       |   ~~~~~~~~^~

Here is the wrapper:

template <typename T>
void _signalCallback(_Bind<Glib::SignalProxy<void()> (T::*(Widget*))()> callback) {
  callback();
  printf("callback\n");
}

template <typename T>
void fromSignal(Gtk::Widget* widget, Glib::SignalProxy<void()> (T::*f)()) {
  auto b = std::bind(f, widget);
  _signalCallback<T>(b);
}


void main() {
  auto button = new Gtk::Button();
  fromSignal<Gtk::Button>(button, &Gtk::Button::signal_clicked);
}

I tried changing the definition of _signalCallabck which then gives me the following error:

void _signalCallback(function<Glib::SignalProxy<void()>()> callback);

// The error message:
error: could not convert 'b' from 'std::_Bind<Glib::SignalProxy<void()> (Gtk::Button::*(Gtk::Widget*))()>' to 'std::function<Glib::SignalProxy<void()>()>'
[build]    26 |   _signalCallback<T>(b);
[build]       |                      ^
[build]       |                      |
[build]       |                      std::_Bind<Glib::SignalProxy<void()> (Gtk::Button::*(Gtk::Widget*))()>

What is the correct way to do this? Am I overly complicating this?

7
  • 1
    Remember that all symbols beginning with an underscore followed by an upper-case letter, like for example _Bind, is a symbol reserved by the compiler and standard library. If you need to use such symbols then I would argue that it's a sign that you're doing something wrong. Commented Jan 27, 2024 at 20:33
  • Yeah, I thought that it looked like an incorrect way to do it. Everywhere else uses the function type as a parameter like my last block of code, but I couldn't get that working. Commented Jan 27, 2024 at 20:35
  • My recommendation is that you look at how the standard library itself does when it comes to callable objects: Plain template argument. As in template<typename F> void _signalCallback(F callback) { /* ... */ }. Similar change to fromSignal. Couple that with lambdas. Commented Jan 27, 2024 at 20:38
  • The gist of your error messages is that bind(f, widget) (is that std:bind?) cannot be invoked with no arguments. Could you provide an explanation of why it should work that way? Maybe write out what the expression would be without the bind? Commented Jan 27, 2024 at 21:56
  • "Am I overly complicating this?" -- Probably, but I don't know anything about your event system, so I cannot be sure. Commented Jan 27, 2024 at 22:02

1 Answer 1

1

Templates make a mess of error messages. I propose three simplifications to your example code.

First, since _signalCallback will just invoke its parameter, it's easy to inline. So _signalCallback<T>(b) becomes b(). One template eliminated. Second, we can replace the fromSignal function template with a function for the specified template argument, Gtk::Button. The other template eliminated. However, there is one more template messing with the error message, namely std::bind.

The third simplification is to drop std::bind and write out the equivalent expression. (I'll also throw in an alias to make the signature easier to read.

using RetType = Glib::SignalProxy<void()>;

void fromSignal(Gtk::Widget* widget, RetType (Gtk::Button::*f)()) {
  (widget->*f)();
}

Compiling this gives a more informative error message:

error: pointer to member type RetType (Gtk::Button::)() {aka 'Glib::SignalProxy<void()> (Gtk::Button::)()'} incompatible with object type Gtk::Widget

The compiler is complaining because you cannot take an arbitrary Widget and expect it to have a member from Button. Base classes cannot be used to invoke a method of a derived class. You need a derived class pointer to invoke a member function of the derived class.

Change the type of the first parameter from Gtk::Widget* to T* so that it corresponds to the type of the second parameter.

template <typename T>
void fromSignal(T* widget, Glib::SignalProxy<void()> (T::*f)()) {
  auto b = std::bind(f, widget);
  _signalCallback<T>(b);
}

A lambda might make the code easier to read.

template <typename T>
void fromSignal(T* widget, Glib::SignalProxy<void()> (T::*f)()) {
  _signalCallback<T>([widget, f]() { return (widget->*f)(); });
}

If you don't like the syntax to invoke a pointer-to-member (which some people find clunky):

template <typename T>
void fromSignal(T* widget, Glib::SignalProxy<void()> (T::*f)()) {
  _signalCallback<T>([widget, f]() { return std::invoke(f, widget); });
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.