Анонимные функции с использованием выражений операторов 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.

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

  • Самый быстрый способ удалить повторяющееся значение из списка лямбдой
  • Группировать по и суммировать объекты, как в SQL с Java lambdas?
  • Завершение сеанса StopWatch с помощью делегата или lambda?
  • Почему java.util.Collection не реализует новый интерфейс Stream?
  • Выражение lambda и общий метод
  • Lambdas в classическом примере enums
  • Я хочу понять выражение lambda в @ Html.DisplayFor (modelItem => item.FirstName)
  • Когда фигурные скобки необязательны в синтаксисе lambda Java 8?
  • java.lang.ClassCastException с использованием lambda-выражений в искровом задании на удаленном сервере
  • Зачем использовать std :: bind over lambdas в C ++ 14?
  • C ++ 11 реализация и модель памяти
  • Давайте будем гением компьютера.