Вернуть указатель функции на вложенную функцию в C

Как уже сказано в заголовке, я пытаюсь объявить вложенную функцию и вернуть указатель на эту функцию. Я хочу, чтобы эта функция «не» возвращала новый указатель на функцию, который вернет отрицание того, чем была исходная функция.

Вот что у меня есть:

someType not( someType original ) {
    int isNot( ListEntry* entry ) {
        return !original( entry );
    }

    someType resultFunc = calloc( 1024, 1 );
    memcpy( resultFunc, &isNot, 1024 );

    return resultFunc;
}

someType определяется как:

typedef int(*someType)(ListEntry* entry)
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
3 834
4

Ответы 4

Я использую GCC.

Вы можете включить вложенные функции с помощью флага:

-fnested-functions

при компиляции.

Я также никогда не слышал о вложенных функциях в C, но если gcc поддерживает это, это не будет работать так, как вы ожидаете. Вы просто копируете машинные инструкции isNot, и они не будут включать фактическое значение «original» во время вызова «not».

Вы должны использовать класс C++ для реализации функциональный объект, в котором хранится указатель, который вы можете инициализировать значением «оригинал» и возвращать экземпляр этого класса из «не».

Стив, у вас совершенно неверная ментальная модель того, что такое функция C.

someType resultFunc = calloc( 1024, 1 );
memcpy( resultFunc, &isNot, 1024 );

Из фрагмента вашего кода я могу предположить, что вы думаете, что можете скопировать скомпилированный код функции в блок памяти, а затем повторно использовать его. Подобные вещи пахнут Лиспом, за исключением того, что даже в шепелявке вы этого не делаете.

Фактически, когда вы говорите «& isNot», вы получаете указатель на функцию. Копирование памяти, на которую указывает указатель, контрпродуктивно - память была инициализирована при загрузке исполняемого файла в память и не меняется. В любом случае, запись someFunc () вызовет дамп ядра, так как память кучи, связанная с someFunc, не может быть выполнена - это защищает вас от всех видов вирусов.

Кажется, вы ожидаете реализации замыканий на C. Этой реализации просто нет. В отличие от Lisp, Perl или Ruby, C не может сохранять элементы фрейма стека после выхода из этого фрейма. Даже если в некоторых компиляторах разрешены вложенные функции, я уверен, что вы не можете ссылаться на неглобальные переменные изнутри этих функций. Закрытие для замыканий - это действительно объект C++, который хранит состояние и реализует operator (), но это совершенно другой подход, и вам все равно придется делать что-то вручную.

Обновление: здесь - соответствующая часть документации GCC. Ищите «Но этот метод работает только до тех пор, пока содержащая функция (взломать, в этом примере) не завершает работу».

спасибо за ссылку, моя любимая цитата была: «Если вы попытаетесь вызвать вложенную функцию через ее адрес после того, как содержащая функция завершится, все вырвется наружу».

luke 23.05.2010 22:56

Вы не сможете делать это так, как хотите. У вас есть несколько альтернативных вариантов.

Вы можете использовать макросы:

#define FN_NOT(F) !F
#define notSomeFunc FN_NOT(someFunc)
...
x = notSomeFunc(entry);

Но я подозреваю, что вы хотели передать отрицательную функцию другим функциям, которые принимают указатели на функции, так что это не сработает.

Вы можете изменить свои интерфейсы, чтобы принимать дополнительную информацию, например

struct closure {
  void *env;
  int (*f)(struct closure* extra, ListEntry*);
};

static int isNot(struct closure* extra, ListEntry *entry) {
  someType original = extra->env;
  return !original(entry);
}

struct closure not(someType original) {
   closure rv;
   rv.env = original;
   rv.f = &isNot;
   return rv;
}

А затем используйте это как:

struct closure inverse_fn;
inverse_fn = not( &fn );
if ( inverse_fn.f(&inverse_fn, entry) ) {
    ...
}

Есть и другие вещи, которые вы можете попробовать, например функции JITing во время выполнения, но такие методы будут зависеть от платформы и архитектуры. Это неудобное решение, но чистый C и переносимый.

Другие вопросы по теме