Reputation: 20370
I would like to use lambdas with g_signal_connect()
from Gtk+.
Traditionally works like this to setup a callback function:
#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);
}
Above test is compiled with g++ $(pkg-config --cflags --libs gtk+-3.0) test.cpp
to get the necessary GTK+-3.0 definitions.
In attempting to convert my_callback1()
to a lambda, I tried variations of this:
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);
}
The test2()
code above produces the following compile error:
/usr/include/glib-2.0/gobject/gclosure.h:70:41: error: invalid cast
from type ‘test2()::<lambda(GtkApplication*, gpointer)>’
to type ‘GCallback {aka void (*)()}’
Is there a way to specify a C++ lambda as the callback function? I don't understand what is needed to fix this "invalid cast".
Upvotes: 5
Views: 1946
Reputation: 25536
Problem is a pointer type mismatch, as the error message clearly tells:
invalid cast from type ‘test2()::<lambda(GtkApplication*, gpointer)>’
^ accepting 2 parameters^
to type ‘GCallback {aka void (*)()}’
^ no parameter...
The more interesting question now would be: Why did the first attempt work then? Well, obviously, the C style cast in the conversion macro acts as reinterpret_cast
to another function pointer. This is possible for raw function pointers, not so, however, with lambdas...
To get around, the lambda first must be casted to appropriate function pointer; you might try this piece of code for illustration:
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;
}
(used C++ cast instead of C style cast here).
Upvotes: 2
Reputation: 170211
When you look at the definition of G_CALLBACK
, you see that it's merely a cast to a void (*)()
. This is Gtk+ employing a form of type erasure on the pointer types it receives, removing the argument list.
The lambda defines a (closure) object type. It's not a function. And while a capture-less lambda does have an implicit conversion operator to a function pointer, that pointer has a signature that matches the lambda's parameter list.
So you may convert the lambda to a void(*)(GtkApplication*, gpointer)
, but not directly to a void (*)()
since it's a completely unrelated type.
The workaround is to make the lambda convert into a function pointer type before feeding it to G_CALLBACK
for the cast. One neat trick that does it, is appending a +
before the lambda:
g_signal_connect(app, "activate",
G_CALLBACK(
+[](GtkApplication *application, gpointer user_data)
{
std::cout << "test2" << std::endl;
}
), data);
Since unary + isn't overloaded for lambdas, the compiler is being helpful and does a conversion to a pointer for us (something unary + may be applied to). After that, the cast in the macro should work.
Upvotes: 14