web-dev-qa-db-fra.com

Comment créer une fonction qui retourne une fonction?

Vue d'ensemble: j'ai un module avec des fonctions et un module avec des procédures et des fonctions sur ces fonctions.

Lorsque je combine deux fonctions (à partir de l'interface de module de la fonction):

double f1(double alpha, double x);
double f2(double beta, double x);

À plusieurs égards (l’un d’eux ajoute):

double OP_Addition(double (*f)(double,double) , double (*g)(double,double), double param1, double param2, double x);

Ne pose aucun problème avec la mise en œuvre suivante:

z1 = (*f)(param1, x);
z2 = (*g)(param2, x);
y = z1 + z2;
return y;

Mais lorsque je veux renvoyer un pointeur sur une "nouvelle" fonction, quelque chose comme:

void *OP_PAdd( double (*f)(double,double), double param3 );

Je ne peux pas le faire fonctionner correctement, ni faire le bon "appel". Je veux avoir utiliser la sortie "fonction" comme entrée dans d'autres fonctions.

40
MCL

Lorsque vous retournez une fonction d'une autre fonction, le moyen le plus propre consiste à utiliser une variable typedef:

typedef double (*ftype)(double, double);

Ensuite, vous pouvez déclarer votre fonction comme ceci:

ftype OP_PAdd( ftype f, double param3 )
{
    ....
    return f1;
}

Vous pouvez le faire sans une typedef, mais c'est compliqué:

double (*OP_PAdd( double (*f)(double,double), double param3 ))(double,double)
{
    return f1;
}

Ainsi, lorsque vous avez des pointeurs de fonction en tant que paramètres ou que vous renvoyez les valeurs d'autres fonctions, utilisez un typedef.

MODIFIER:

Bien que vous puissiez déclarer le type comme ceci:

typedef double ftype(double, double);

Vous ne pouvez jamais utiliser directement un type de ce type dans la pratique. Une fonction ne peut pas renvoyer de fonction (uniquement un pointeur sur une fonction) et une variable de ce type ne peut pas être affectée. 

De plus, vous n'avez pas besoin de déréférencer explicitement un pointeur de fonction pour l'appeler, le fait que le pointeur lui-même soit masqué n'est donc pas un gros problème. C'est aussi une convention de définir les pointeurs de fonction comme typedef. Dans la page de manuel de signal :

   #include <signal.h>

   typedef void (*sighandler_t)(int);

   sighandler_t signal(int signum, sighandler_t handler);
32
dbush

Voulez-vous dire quelque chose comme ça? La fonction decider() renvoie un pointeur sur une autre fonction, qui est ensuite appelée.

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

typedef double(*fun)(double, double);

double add(double a, double b) {
    return a + b;
}

double sub(double a, double b) {
    return a - b;
}

double mul(double a, double b) {
    return a * b;
}

fun decider(char op) {
    switch(op) {
        case '+': return add;
        case '-': return sub;
        case '*': return mul;
    }
    exit(1);
}

int main(void)
{
    fun foo;

    foo = decider('+');
    printf("%f\n", foo(42.0, 24.0));

    foo = decider('-');
    printf("%f\n", foo(42.0, 24.0));

    foo = decider('*');
    printf("%f\n", foo(42.0, 24.0));

    return 0;
}

Sortie du programme:

66.000000
18.000000
1008.000000

EDIT: Suite aux commentaires sous @dbush answer , cette version revient de la typedef en tant que pointeur, à une simple fonction. Il donne la même sortie, mais dans decider(), il se compile proprement et donne la sortie correcte, que j'écrive return add; ou return &add;

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

typedef double(fun)(double, double);

double add(double a, double b) {
    return a + b;
}

double sub(double a, double b) {
    return a - b;
}

double mul(double a, double b) {
    return a * b;
}

fun *decider(char op) {
    switch(op) {
        case '+': return add;     // return &add;
        case '-': return sub;
        case '*': return mul;
    }
    exit(1);
}

int main(void)
{
    fun *foo;

    foo = decider('+');
    printf("%f\n", foo(42.0, 24.0));

    foo = decider('-');
    printf("%f\n", foo(42.0, 24.0));

    foo = decider('*');
    printf("%f\n", foo(42.0, 24.0));

    return 0;
}
13
Weather Vane

en C, vous pouvez retourner le pointeur à fonction, mais pour ce faire, la fonction doit exister en premier, et la création dynamique de fonctions n’est pas quelque chose que C dit possible, peu importe comment le faire

si votre code ne fonctionne que sur un système d'exploitation et un processeur (et probablement d'autres restrictions), vous pourrez peut-être:

  1. allouer une page de mémoire
  2. écrivez des données et du code machine en faisant ce que vous voulez, en appelant des fonctions passées par un pointeur, etc.
  3. change la protection de la mémoire de lecture/écriture à lecture/exécution
  4. retourne le pointeur sur la fonction créée
  5. ne vous inquiétez pas du fait que vous avez besoin de 4 Ko par fonction

il y a probablement des bibliothèques quelque part pour cela, mais pas nécessairement portable

3
Jordan Szubert

Donc, vous voulez qu'une fonction retourne un pointeur sur une fonction.

double retfunc()
{
   return 0.5;
}

double (*fucnt)()
{
  return retfunc;
}

main()
{
   printf("%f\n", (*funct())());
}
0
Bing Bang

Puisque certaines personnes semblent apparemment paranoïaques pour écrire un hack pour résoudre ce problème, voici une façon moins astucieuse de le faire: Utilisez une structure statique avec setjmp et longjmp.

jmp_buf jb;

void *myfunc(void) {
    static struct {
        // put all of your local variables here.
        void *new_data, *data;
        int i;
    } *_;
    _ = malloc(sizeof(*_));
    _.data = _;
    if (!(_.new_data = (void *)(intptr_t)setjmp(jb)))
        return _.data;
    _.data = _.new_data;
    /* put your code here */
    free(_);
    return NULL;
}

Pour expliquer ce qui se passe ici, setjmp renverra une valeur de 0 lors de la création de la mémoire tampon de saut. Sinon, la valeur transmise par longjmp sera renvoyée (par exemple, longjmp (jb, 5) fera que setjmp retournera 5).

Donc, ce que nous faisons, c'est que notre fonction renvoie un pointeur sur la structure de données qui lui est allouée. puis en appelant notre fermeture comme:

void *data = myfunc();
longjmp(jb, (int)(intptr_t)data);

Veuillez noter qu’un int n’est pas suffisamment grand pour stocker un pointeur sur toutes les plates-formes; vous devrez donc peut-être créer un pool de données et renvoyer/transmettre les données par descripteur (index dans le pool).

Comme je l'ai déjà dit, une fermeture est simplement une fonction avec toutes ses données allouées sur le tas.

J'écris des hacks pour les jeux N64 et PSP depuis des années. Les gens qui prétendent que c'est impossible n'ont probablement jamais bricolé avec ce genre de choses. Le plus souvent, c’est un manque d’expérience.

0
DeftlyHacked