Существует ли ISO C-совместимый способ сделать функцию, которая возвращает указатель на функцию, вложенную внутрь? - PullRequest
1 голос
/ 02 октября 2019
#include <stdio.h>
int (*function_factory(int outer_args))(int){ 
    int returned_function (int inner_args){ 
        return outer_args + inner_args;     
    }
    return &returned_function;
}

int main(void){                
    printf("%d\n",function_factory(1)(2)); 
    return 0; 
}

Когда я пытаюсь скомпилировать приведенный выше код с флагами -pedantic и -Werror, я получаю ошибку error: ISO C forbids nested functions. В целях моей задачи, предположим, что я не могу удалить эти два флага. Есть ли способ, которым я могу вернуть функцию, которая будет принимать inner_args, все еще имея доступ к external_args?

Ответы [ 3 ]

4 голосов
/ 02 октября 2019

Хотя GNU C поддерживает вложенные функции, все равно не будет работать. Вложенные функции в GNU C являются «только нисходящими funarg». Они не лексические замыкания;все в стеке. Когда родительский элемент вложенной функции завершается, эта функция становится мусором;использование возвращенного указателя функции вне этой области не является четко определенным.

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

Чтобы моделировать лексические замыкания в стандарте C, необходимо поднять все тела функций в область действия файла (без вложенности) и использовать явную структуру данных замыкания, котораясодержит указатель на код и на захваченную среду:

#include <stddef.h>

#include <stdlib.h>
#include <stdio.h>

struct counting_closure {
  /* simulated captured environment */
  int count;
  /* pointer to code */
  int (*lambda)(struct counting_closure *);
};

int counting_lambda(struct counting_closure *clo)
{
   /* simulated access to captured lexical */
   return clo->count++;
}

struct counting_closure *make_counter(int start)
{
   struct counting_closure *clo = malloc(sizeof *clo);
   clo->count = start;
   clo->lambda = counting_lambda;
   return clo;
}

int main(void)
{
   struct counting_closure *cntr = make_counter(1);
   printf("%d\n", cntr->lambda(cntr));
   printf("%d\n", cntr->lambda(cntr));
   printf("%d\n", cntr->lambda(cntr));
   free(cntr);
   return 0;
}
0 голосов
/ 02 октября 2019

Нет. Язык C не имеет вложенных функций, и даже расширение «GNU C» для них не позволяет вам вызывать их после завершения вызова включающей функции.

Правильный и переносимый способ сделать это -C с парой (возможно, хранящейся в структуре, если хотите) указателя на функцию и указателя на структуру, содержащую контекст. Затем контекст может быть размещен в стеке «вызова функции включения», если вы не будете использовать контекст после завершения вызова;в противном случае он может находиться в выделенном хранилище (malloc), которое вы явно освобождаете, когда делаете с ним.

Такая пара не может быть передана интерфейсам, которые принимают только указатель на функцию, но хорошие API, использующие указатели на функции, такжепринять (обычно void *) контекстный аргумент для передачи обратно в функцию.

0 голосов
/ 02 октября 2019

Вы не можете иметь вложенные функции, но вы можете вернуть указатель на функцию:

typedef int (*FuncPtr)(void);

int sample(void){
    return 1;
}

FuncPtr choose(int f)
{
    if (f == 1) return sample;
    return NULL;
}

main()
{
    FunPtr p = choose(1);
    if (p) { printf("%d\n", p()); }
    return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...