我正在尝试为 Gtk 单击信号制作一个包装器以挂钩到我的事件系统。 Gtk 处理点击事件的方式是这样的:
auto button = new Gtk::Button();
button->signal_clicked().connect(...);
因此,为了将其添加到我的系统中,我创建了一个名为
fromSignal
的函数,它接受两个输入,我将它们绑定在一起。我遇到的问题是在callback();
线上。
error: no match for call to '(std::_Bind<Glib::SignalProxy<void()> (Gtk::Button::*(Gtk::Widget*))()>) ()'
[build] 18 | callback();
[build] | ~~~~~~~~^~
这是包装纸:
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);
}
我尝试更改
_signalCallabck
的定义,然后出现以下错误:
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*))()>
正确的做法是什么?我是不是把这个问题搞得太复杂了?
模板会产生混乱的错误消息。我建议对您的示例代码进行三种简化。
首先,由于
_signalCallback
只会调用它的参数,因此很容易内联。所以_signalCallback<T>(b)
变成了b()
。消除了一个模板。其次,我们可以将 fromSignal
函数模板替换为指定模板参数 Gtk::Button
的函数。另一个模板被淘汰。然而,还有一个模板与错误消息混淆,即 std::bind
。
第三种简化是去掉
std::bind
并写出等价表达式。 (我还将添加一个别名,以使签名更易于阅读。
using RetType = Glib::SignalProxy<void()>;
void fromSignal(Gtk::Widget* widget, RetType (Gtk::Button::*f)()) {
(widget->*f)();
}
编译它会给出更丰富的错误消息:
错误:指向成员类型的指针
{aka 'Glib::SignalProxyRetType (Gtk::Button::)()
(Gtk::Button::)()'} 与对象类型不兼容 Gtk::Widget
编译器正在抱怨,因为您不能采用任意
Widget
并期望它具有来自 Button
的成员。基类不能用于调用派生类的方法。您需要一个派生类指针来调用派生类的成员函数。
将第一个参数的类型从
Gtk::Widget*
更改为 T*
,使其与第二个参数的类型对应。
template <typename T>
void fromSignal(T* widget, Glib::SignalProxy<void()> (T::*f)()) {
auto b = std::bind(f, widget);
_signalCallback<T>(b);
}
lambda 可能会使代码更易于阅读。
template <typename T>
void fromSignal(T* widget, Glib::SignalProxy<void()> (T::*f)()) {
_signalCallback<T>([widget, f]() { return (widget->*f)(); });
}