Анонимные функции с использованием выражений операторов 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, но другие впечатления или мнения были бы замечательными). Будет ли это испортить статические анализаторы?
Ответы
Ответ 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.
Для отладки это будет очень сложно. Большинство отладчиков не могут отслеживать макрос. Скорее всего, вам придется отлаживать сборку.