Используйте лямбда-выражения С++ 11 в качестве обратных вызовов в библиотеке GObject.

Лямбда-выражения С++ 11, которые ничего не фиксируют, могут быть сохранены в указателе функции. Просто нужно убедиться, что лямбда принимает и возвращает те же параметры, что и указатель функции.

В библиотеке GObject все обратные вызовы имеют тип void(*GCallback) (void). Однако это определение никоим образом не влияет на подпись обратного вызова:

Тип, используемый для функций обратного вызова в определениях структур и сигнатурах функций. Это не означает, что все функции обратного вызова не должны принимать никаких параметров и возвращать значение void. Требуемая сигнатура функции обратного вызова определяется контекстом, в котором она используется (например, сигналом, к которому она подключена). Используйте G_CALLBACK(), чтобы привести функцию обратного вызова к GCallback.

Другими словами, можно передать функцию следующим образом:

int my_function(int a, char b) {}

путем приведения его типа (это то, что делает G_CALLBACK):

do_something(G_CALLBACK(my_function));

К сожалению, приведение типов не работает с лямбда-выражениями С++ 11:

do_something(G_CALLBACK([](int a, char b) -> int {...});
// Cannot cast from type lambda to pointer type GCallback

Можно ли вместо GCallback использовать лямбда-выражения С++ произвольного типа?

ОБНОВЛЕНИЕ

Просто чтобы уточнить, я знаю, что лямбда может быть приведена к указателю на функцию, если их подписи совпадают. Мой вопрос в другом измерении.

Стандарт ISO C гарантирует, что функция может выполняться вперед и назад без потери точности. Другими словами, справедливо одно из следующих выражений:

int f(int a){...}

void (*void_f)() = (void (*)())f;
int (*restored_f)(int) = (int (*)(int))void_f;
restored_f(10);

Мой вопрос заключается в том, допустимо ли следующее выражение в соответствии с С++ 11:

int (*f)(int) = [](int a) -> int {};
void (*void_f)() = (void (*)())f;
int (*restored_f)(int) = (int (*)(int))void_f;
restored_f(10);

person Kentzo    schedule 21.02.2014    source источник
comment
Я сам не пробовал, но вы можете попробовать сохранить лямбду как переменную (auto fun = [](...){...};), а затем выполнить явное приведение в вызове do_something (do_something(reinterpret_cast<void(*)()>(fun));). Поскольку я не пробовал, я понятия не имею, будет ли это работать или нет.   -  person Some programmer dude    schedule 21.02.2014
comment
stackoverflow.com/questions/15657011/   -  person SomeWittyUsername    schedule 21.02.2014
comment
@icepack этот вопрос не о том, как использовать лямбда-выражения вместо указателей на функции. Речь идет о том, как (если возможно) использовать лямбду произвольного типа вместо GCallback.   -  person Kentzo    schedule 21.02.2014
comment
@Кентзо Я понимаю это. Если вы внимательно посмотрите на этот ответ, вы увидите, что не всегда возможно поменять местами лямбда-выражения для указателей C.   -  person SomeWittyUsername    schedule 21.02.2014


Ответы (2)


Лямбды без захвата неявно преобразуемы на указатель на функцию по стандарту. Хотя на данный момент не все компиляторы поддерживают эту функцию (https://stackoverflow.com/a/2935230/261217).

Затем вы можете явно привести указатель функции к GCallback.

person Mikhail    schedule 21.02.2014
comment
Пожалуйста, смотрите обновление моего вопроса. Допустимо ли второе выражение для C++11? Если да, то не могли бы вы указать стандарт? - person Kentzo; 21.02.2014
comment
Я не вижу проблемы в этом преобразовании, лямбда-функция - это просто метод анонимного класса. Она существует так же, как и обычная функция. Я не могу указать на стандарт, извините, я слишком ленив :) - person Mikhail; 21.02.2014

Следующий код компилируется и работает у меня (MSVC 2013):

auto lambdaFunc = [](int a, char b) -> int { return 0; };

typedef int (*LambdaType)(int, char);

GCallback fnTest1 = G_CALLBACK((LambdaType)lambdaFunc);
GCallback fnTest2 = G_CALLBACK((LambdaType) [](int a, char b) -> int { return 0; });

do_something(fnTest1);
do_something(fnTest2);
do_something(G_CALLBACK((LambdaType)lambdaFunc));
person Oleg    schedule 21.06.2017