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

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

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

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

#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__; }); 

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

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

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

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

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

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

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

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

    1. вложенные функции
    2. выражения оператора

    Обе вложенные функции (определяющие функцию внутри составного оператора) и выражения операторов ( ({}) , в основном блок, который дает значение), не разрешены в 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) ), вы получите это. Но я мог ошибаться в этом.

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

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

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

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

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

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

    Давайте будем гением компьютера.