我想使用来自Gtk +的g_signal_connect()
的lambdas。
传统上这样设置回调函数:
#include <gtk/gtk.h>
#include <iostream>
void my_callback(GtkApplication *app, gpointer user_data)
{
std::cout << "test1" << std::endl;
}
void test1()
{
GtkApplication *app = gtk_application_new(nullptr, G_APPLICATION_NON_UNIQUE);
void * data = nullptr; // simple example
g_signal_connect(app, "activate", G_CALLBACK(my_callback), data);
}
上面的测试是用g++ $(pkg-config --cflags --libs gtk+-3.0) test.cpp
编译的,以获得必要的GTK + -3.0定义。
在尝试将my_callback1()
转换为lambda时,我尝试了以下变体:
void test2()
{
GtkApplication *app = gtk_application_new(nullptr, G_APPLICATION_NON_UNIQUE);
void * data = nullptr; // simple example
// use lambda instead of call to explicit function
g_signal_connect(app, "activate",
G_CALLBACK(
[](GtkApplication *application, gpointer user_data)
{
std::cout << "test2" << std::endl;
}
), data);
}
上面的test2()
代码会产生以下编译错误:
/usr/include/glib-2.0/gobject/gclosure.h:70:41: error: invalid cast
from type ‘test2()::<lambda(GtkApplication*, gpointer)>’
to type ‘GCallback {aka void (*)()}’
有没有办法将C ++ lambda指定为回调函数?我不明白修复这个“无效演员”需要什么。
答案 0 :(得分:5)
当您查看G_CALLBACK
的定义时,您会发现它仅仅是对void (*)()
的强制转换。这是Gtk +在它接收的指针类型上采用了一种类型的擦除形式,删除了参数列表。
lambda定义了一个(闭包)对象类型。它不是一个功能。虽然无捕获的lambda确实有一个函数指针的隐式转换运算符,但该指针有一个与lambda参数列表匹配的签名。
因此,您可以将lambda转换为void(*)(GtkApplication*, gpointer)
,但不能直接转换为void (*)()
,因为它是完全不相关的类型。
解决方法是将lambda转换为函数指针类型,然后将其转换为G_CALLBACK
以进行转换。一个巧妙的技巧就是在lambda:
+
g_signal_connect(app, "activate",
G_CALLBACK(
+[](GtkApplication *application, gpointer user_data)
{
std::cout << "test2" << std::endl;
}
), data);
由于一元+没有为lambdas重载,编译器很有用,并为我们转换为指针(可以应用一元+)。在那之后,宏中的强制转换应该可以工作。
答案 1 :(得分:1)
问题是指针类型不匹配,因为错误消息清楚地告诉:
invalid cast from type ‘test2()::<lambda(GtkApplication*, gpointer)>’
^ accepting 2 parameters^
to type ‘GCallback {aka void (*)()}’
^ no parameter...
现在更有趣的问题是:为什么第一次尝试工作呢?嗯,显然,转换宏中的C样式作为另一个函数指针的reinterpret_cast
。对于原始函数指针,这是可能的,但是,对于lambdas ...
要绕过,首先必须将lambda转换为适当的函数指针;你可以尝试这段代码来说明:
int main(int argc, char* argv[])
{
void(*f)(void) = reinterpret_cast<void(*)(void)>
(
static_cast<void(*)(int)>([](int n){ std::cout << n; })
);
reinterpret_cast<void(*)(...)>(f)(7);
std::cout << std::endl;
return 0;
}
(在这里使用C ++强制转换而不是C样式转换)。