Gtk+ g_signal_connect() 和 C++ lambda 会导致"invalid cast"错误



我想从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指定为回调函数?我不明白修复这种"无效的演员"需要什么。

当您查看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的Unary 没有超载,因此编译器会有所帮助,并为我们提供指针(可以应用于Unary (。之后,宏中的铸件应起作用。

问题是指针类型的不匹配,正如错误消息清楚地说明:

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样式(。

相关内容

最新更新