Вернуть указатель на вложенную функцию в C - PullRequest
1 голос
/ 03 сентября 2008

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

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

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)

Ответы [ 4 ]

19 голосов
/ 03 сентября 2008

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

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

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

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

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

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

1 голос
/ 08 февраля 2009

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

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

#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 и портативно.

0 голосов
/ 03 сентября 2008

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

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

0 голосов
/ 03 сентября 2008

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

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

-fnested-functions

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...