Анонимные функции с использованием выражений операторов GCC
Этот вопрос не очень специфичен; это действительно для моего собственного обогащения C, и я надеюсь, что другие могут найти его полезным.
Отказ от ответственности: я знаю, что у многих будет импульс, чтобы ответить «если вы пытаетесь сделать FP, тогда просто используйте функциональный язык». Я работаю во встроенной среде, которая должна связываться со многими другими библиотеками C и не имеет большого пространства для многих более крупных общих библиотек и не поддерживает многие языковые процессы. Более того, динамическое распределение памяти не может быть и речи. Мне тоже очень любопытно.
Многие из нас видели этот отличный макрос макроса для lambda-выражений:
- Linq-запрос или выражение Lambda?
- Java 8 Streams: несколько фильтров или сложное условие
- Справочник по методу экземпляра и lambda-параметры
- Передача lambda в качестве указателя функции
- Maven. lambda-выражения не поддерживаются в источнике 1.5
#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
, lambda расширяется до:
int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });
Итак, это мои вопросы:
-
Что именно делает прямую int (* X); объявлять? Конечно, int * X; является указателем на целое число, но как эти два отличаются?
-
Взглянув на преувеличенный макрос, что же делает последний
__fn__
? Если я пишу тестовую функциюvoid test() { printf("hello"); } test;
void test() { printf("hello"); } test;
– это немедленно вызывает ошибку. Я не понимаю этого синтаксиса. -
Что это значит для отладки? (Я планирую экспериментировать с этим и gdb, но другие впечатления или мнения были бы замечательными). Будет ли это испортить статические анализаторы?
- Как преобразовать дерево выражений в частичный запрос SQL?
- Почему lambda C ++ 11 требует по умолчанию «изменяемого» ключевого слова для захвата по значению?
- Есть ли способ сравнить lambda?
- Linq - максимальное значение для каждой группы
- Невозможно использовать параметр ref или out в lambda-выражениях
- Какой тип lambda выведен с помощью «auto» в C ++ 11?
- Использование lambda-выражений для обработчиков событий
- Потоки последовательного и параллельного выполнения streamов Java8 дают разные результаты?
Это объявление (в области блока):
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
.
Ваш lambda-макрос использует две функциональные функции. Сначала он использует вложенные функции для фактического определения тела вашей функции (поэтому ваша lambda не является анонимной, она просто использует неявную переменную __fn__
(которую следует переименовать в нечто другое, так как имена с двойным лидирующим символом зарезервированы для компилятора , возможно, что-то вроде yourapp__fn__
будет лучше).
Все это само выполняется в составном заявлении GCC (см. http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs ), основной формат которого выглядит примерно так:
({ ...; retval; })
последний оператор составного оператора является адресом только что объявленной функции. Теперь int (*max)(int,int)
просто получает значение составного оператора, который теперь является указателем на только что объявленную функцию анонимности.
Конечно, отладочные macros – это королевская боль.
Что касается причины, почему test;
.. по крайней мере здесь, я получаю «тест, переопределенный как другой тип символа», который, как я полагаю, означает, что GCC рассматривает его как объявление, а не (бесполезное) выражение. Поскольку нетипизированные переменные по умолчанию для int
и потому, что вы уже объявили test
как функцию (по существу, void (*)(void)
), вы получите это. Но я мог ошибаться в этом.
Тем не менее, это не переносимо никаким протяжением воображения.
-
int (*max)(int, int)
– тип переменной, которую вы объявляете. Он определяется как указатель функции с именем max, который возвращает int и принимает два значения в качестве параметров. -
__fn__
относится к имени функции, которое в этом случае является макс. -
У меня нет ответа. Я бы предположил, что вы можете пройти через него, если вы запустили его через препроцессор.
Частичный ответ: это не int (* X), который вас интересует. Это int (* X) (y, z). Это указатель на функцию X, которая принимает (y, z) и возвращает int.
Для отладки это будет очень сложно. Большинство отладчиков не могут отслеживать макрос. Скорее всего, вам придется отлаживать сборку.