LINUX.ORG.RU

Вопрос о замыканиях

 


0

3

Всем привет.

typedef void(signature)(int a);
...
signature* f = [] (int a) {};
Это работает, но как только я пытаюсь передать в тело f замыкание какой-нибудь переменной:
typedef void(signature)(int a);
...
int b;
signature* f = [&b] (int a) {};
- получаю ошибку. Каким образом в f можно передать замыкание некой переменной (предположим, что её срок жизни длиннее срока жизни лямбды) и можно ли вообще это сделать без изменения сигнатуры?

★★★★★

Это невозможно. Только лямбды без замыканий разлагаются до указателя на функцию. Если есть переменные, то нужен весь объект (а не статическая функция в нём) и его превратить в указатель нельзя.

xaizek ★★★★★
()
Ответ на: комментарий от xaizek

Ага, нашёл кусок об этом в стандарте. Спасибо.

jcd ★★★★★
() автор топика
Ответ на: комментарий от Pavval

Не могу, сигнатура лежит в сишной либе. Её можно изменить, но не до такой степени, чтобы вкорячить туда std::function.

jcd ★★★★★
() автор топика
Ответ на: комментарий от jcd

Тады только через жопу. Лямбда с захватом окружения реализуется как function object. Без захвата - как функция. Потому кроме как через глобальные переменные передать контекст - никак. И у этого подхода куча недостатков.

Pavval ★★★★★
()
Ответ на: комментарий от NeXTSTEP

Из недостатков: нестандарт, не поддерживается в гнутом C++, поэтому придётся сделать обёртку на C.

Главный недостаток, что если принимающая сторона сохранит указатель (а это обычно и нужно), то при выходе из родительской функции и последующем вызове по указателю будут использованы мусорные данные.

anonymous
()
Ответ на: комментарий от NeXTSTEP

(предположим, что её срок жизни длиннее срока жизни лямбды)

Хотя не заметил, что ТС это устраивает.

anonymous
()
typedef std::function<bool(int)> signature;
signature g = [&](int a) {return true;};
Dudraug ★★★★★
()
Ответ на: комментарий от jcd

так в сишной либе

typedef void(signature)(int a);

А в с++ коде

typedef std::function<signature> _signature;

Или ты хочешь в с++11 контексте вызывать лямбды с захватом окружения? Ну тогда только через оберточку.

Dudraug ★★★★★
()
Ответ на: комментарий от NeXTSTEP

Набросал прототип.

foo.c:

/* сишная либа */
typedef void(fun)(int);
void call(fun f) {
    f(3);
}

/* сишная обёртка с использованием nested functions */
typedef void(fun1)(void *, int);
void call_with(void *item, fun1 f1) {
    void f(int i) {
        f1(item, i);
    }
    call(f);
}

bar.cpp:

/* объявления для сишной обёртки */
typedef void(fun1)(void *, int);
extern "C"
void call_with(void *item, fun1 f1);

/* код на крестах с замыканиями */
#include <iostream>
#include <functional>

typedef std::function<void(int)> fun2;

extern "C"
void call_funpp(void *ptr, int n) {
    (*(fun2*)ptr)(n);
}

int main(int argc, char **argv) {
    auto fpp = [&](int n) {
        std::cout << "n=" << n << " argc=" << argc << "\n";
    };
    fun2 fpp1 = fpp;

    call_with((void*)&fpp1, &call_funpp);

    return 0;
}

$ gcc -Wno-trampolines foo.c -c && g++ -std=c++14 bar.cpp foo.o && ./a.out 1 2 3
n=3 argc=4
NeXTSTEP ★★
()
Ответ на: Набросал прототип. от NeXTSTEP

В nested functions не могу, т.к. используем g++

Аккуратненько заменил сишный typedef на std::function в надежде, что никто не заметит :)

jcd ★★★★★
() автор топика

typedef void(signature)(int a);

typedef void(signature)(void* context, int a);
signature* f = [] (void* context, int a) {};
andreyu ★★★★★
()
Последнее исправление: andreyu (всего исправлений: 1)

Для решения этой проблемы используется trampoline-подход: на ходу генерится функция, которая подставляет некоторые аргументы для уже существующей функции. Одна из реализаций называется libffi closure.

vzzo ★★★
()
Ответ на: комментарий от vzzo

Любопытно, не знал, спасибо за наводку.

jcd ★★★★★
() автор топика
Ответ на: комментарий от jcd

Аккуратненько заменил сишный typedef на std::function в надежде, что никто не заметит :)

эпичненько. не думал устроится кирпичеподавальщиком на стройке? или бетономешальщиком? там такие нужны, намного более интересная работа.

anonymous
()
Ответ на: комментарий от anonymous

там такие нужны, намного более интересная работа.

Не иначе как знаешь по личному опыту?

jcd ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.