Подтвердить что ты не робот

Анонимные функции с использованием выражений операторов GCC

Этот вопрос не очень специфичен; это действительно для моего собственного обогащения C, и я надеюсь, что другие могут найти его полезным.

Отказ от ответственности: я знаю, что у многих будет импульс, чтобы ответить "если вы пытаетесь сделать FP, тогда просто используйте функциональный язык". Я работаю во встроенной среде, которая должна связываться со многими другими библиотеками C и не имеет большого пространства для многих более крупных общих библиотек и не поддерживает многие языковые процессы. Более того, динамическое распределение памяти не может быть и речи. Мне тоже очень любопытно.

Многие из нас видели этот отличный макрос макроса для лямбда-выражений:

#define lambda(return_type, function_body) \
({ \
      return_type __fn__ function_body \
          __fn__; \
})

И пример использования:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example

Используя gcc -std=c89 -E test.c, лямбда расширяется до:

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });

Итак, это мои вопросы:

  • Что конкретно делает строка int (* X); объявлять? Конечно, int * X; является указателем на целое число, но как эти два отличаются?

  • Взглянув на exapnded macro, что делает последний __fn__? Если я пишу тестовую функцию void test() { printf("hello"); } test; -, которая сразу же выдает ошибку. Я не понимаю этот синтаксис.

  • Что это значит для отладки? (Я планирую экспериментировать с этим и gdb, но другие впечатления или мнения были бы замечательными). Будет ли это испортить статические анализаторы?

4b9b3361

Ответ 1

Это объявление (в области блока):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });

не является C, но действителен GNU C.

Он использует два расширения gcc:

Обе вложенные функции (определяющие функцию внутри составного оператора) и выражения операторов (({}), в основном блок, который дает значение) в C не допускаются и выходят из GNU C.

В выражении оператора последний оператор выражения является значением конструкции. Вот почему вложенная функция __fn__ появляется в выражении выражения в конце выражения оператора. Обозначение функции (__fn__ в последнем выражении выражения) в выражении преобразуется в указатель на функцию обычными преобразованиями. Это значение, используемое для инициализации указателя функции max.

Ответ 2

Ваш лямбда-макрос использует две функциональные функции. Сначала он использует вложенные функции для фактического определения тела вашей функции (поэтому ваша лямбда на самом деле не анонимна, она просто использует неявную переменную __fn__ (которую следует переименовать в нечто другое, так как имена двузначных символов подчеркивания зарезервированы для компилятор, возможно, что-то вроде yourapp__fn__ будет лучше).

Все это само выполняется в составном заявлении GCC (см. http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs), основной формат которого выглядит примерно так:

({ ...; retval; })

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

Отладка макросов - это, конечно же, королевская боль.

По причине, почему test;.. по крайней мере здесь, я получаю "тест, переопределенный как другой тип символа", который, как я полагаю, означает, что GCC рассматривает его как объявление, а не (бесполезное) выражение. Поскольку нетипированные переменные по умолчанию равны int, и поскольку вы уже объявили test как функцию (по существу, void (*)(void)), вы получите это. Но я мог ошибаться.

Это не переносимо никаким протяжением воображения.

Ответ 3

  • int (*max)(int, int) - тип переменной, которую вы объявляете. Он определяется как указатель функции с именем max, который возвращает int, и принимает два значения в качестве параметров.

  • __fn__ относится к имени функции, которое в этом случае является максимальным.

  • У меня нет ответа. Я бы предположил, что вы можете пройти через него, если вы запустили его через препроцессор.

Ответ 4

Частичный ответ: Это не int (* X), ​​в котором вас интересует. Это int (* X) (y, z). Это указатель на функцию, называемую X, которая принимает (y, z) и возвращает int.

Для отладки это будет очень сложно. Большинство отладчиков не могут отслеживать макрос. Скорее всего, вам придется отлаживать сборку.