39 votes

Comment puis-je faire une fonction qui retourne une fonction?

Grande image: j'ai un module avec des fonctions et un module avec les procédures et les fonctions de ces fonctions.

Quand je combine les deux fonctions (à partir de la fonction du module d'interface):

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

De plusieurs façons, (l'un d'eux est l'ajout d'):

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

Donne pas de problème avec la suivante (morceau de) mise en œuvre:

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

Mais quand je veux retourner un pointeur vers une "nouvelle" fonction, quelque chose comme:

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

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

32voto

dbush Points 8590

Lors du retour d'une fonction à partir d'une autre fonction, la façon la plus propre de le faire est avec un typedef:

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

Vous pouvez déclarer votre fonction comme ceci:

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

Vous pouvez le faire sans un typedef, mais c'est salissant:

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

Alors, quand vous avez la fonction pointeurs comme paramètres ou des valeurs de retour des fonctions, utiliser un typedef.

EDIT:

Alors que vous pourriez déclarer le type comme ceci:

typedef double ftype(double, double);

Vous ne pouvez jamais utiliser directement un type comme ça dans la pratique. Une fonction ne peut pas retourner une fonction (seulement un pointeur vers une fonction), et une variable de ce type ne peuvent pas être affectés.

Aussi, vous ne devez pas explicitement de déréférencement d'un pointeur de fonction pour appeler la fonction, de sorte que le fait que le pointeur lui-même est caché n'est pas un gros problème. C'est également la convention de définir des pointeurs de fonction en tant que typedef. À partir de la page de man pour signal:

   #include <signal.h>

   typedef void (*sighandler_t)(int);

   sighandler_t signal(int signum, sighandler_t handler);

25voto

Basile Starynkevitch Points 67055

D'autres réponses sont correctes, et intéressant, mais vous devez être conscient que, dans portable C99, il n'y a aucun moyen d'avoir de véritables fermetures comme les fonctions C (et c'est un des fondamentaux de la limitation de C). Si vous ne sont pas conscients de ce que les fermetures sont, de lire la page wiki avec soin sur eux (et aussi lire SICP, notamment son §1.3). Notez toutefois que, dans C++11 vous avez des fermetures, en utilisant std::function et lambda-expressions. Et la plupart des autres langages de programmation (Ocaml, Haskell, Javascript, Lisp, Clojure, Python, ....) ont des fermetures.

En raison de l'absence d'une véritable fermetures en C ("mathématiquement" le seul fermée valeurs dans les fonctions C sont des variables globales ou statiques ou des littéraux), la plupart des bibliothèques accepter C pointeurs de fonction de fournir une API de manipulation des rappels avec certaines données du client (un exemple simple pourrait être qsort_r, mais plus sérieusement regarder à l'intérieur de GTK). Que les données du client (généralement un pointeur opaque) peut être utilisé pour maintenir fermé valeurs. Vous voulez probablement de suivre une convention similaire (donc systématiquement passer le pointeur de fonction en tant que rappels, ainsi que quelques autres données du client), de sorte que vous aurez besoin de modifier les signatures de vos fonctions C (au lieu de passer juste un cru de pointeur de fonction, vous allez passer un pointeur de fonction et de certaines données du client comme un rappel, pour "émuler" fermetures).

Vous peut parfois générer une fonction C au moment de l'exécution (à l'aide de caractéristiques non standard, probablement avec l'aide du système d'exploitation ou de certains de bibliothèque externe). Vous pourriez utiliser certains JIT compiler la bibliothèque comme GNU foudre, libjit (les deux serait de générer une certaine lenteur de l'exécution de code rapidement), asmjit (vous allez générer chaque instruction machine explicitement, et il est de votre responsabilité d'émettre rapide x86-64 du code), GCCJIT ou LLVM (les deux sont au-dessus de compilateurs existants peuvent donc être utilisés pour emit un peu lentement - certains code optimisé). Sur POSIX & systèmes Linux, vous pouvez également émettre un peu de code C dans certains fichier temporaire /tmp/tempcode.c, fourche à la compilation (par exemple, gcc -fPIC -Wall -O2 -shared /tmp/tempcode.c -o /tmp/tempcode.so) de ce code dans un plugin, et de charger dynamiquement qui a généré un plugin à l'aide dlopen(3) & dlsym(3)..

BTW, nous ne savons pas ce que le réel de l'application, vous êtes le codage est, mais vous pourriez envisager l'incorporation à l'intérieur de certains interprète, par exemple, Lua ou la Ruse. Vous pourrez ensuite utiliser et fournir des rappels à l'évaluateur embarqué/interprète.

13voto

Weather Vane Points 13830

Voulez-vous dire quelque chose de ce genre? L' decider() fonction renvoie un pointeur vers une autre fonction, qui est alors 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;
}

Programme de la sortie:

66.000000
18.000000
1008.000000

EDIT: Suite aux commentaires sous l' @dbush réponse, cette version de recul par rapport à l' typedef comme un pointeur à une fonction. Il donne le même résultat, mais en decider() il compile correctement et donne de bons résultats, peu importe si j'écris 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;
}

3voto

Jordan Szubert Points 76

en C, vous pouvez retourner le pointeur de fonction, mais pour ce faire, la fonction doit exister d'abord, et de créer dynamiquement des fonctions n'est pas quelque chose dit C est possible, jamais l'esprit comment le faire

si votre code est d'aller travailler que sur un OS et un processeur (et probablement quelques autres restrictions), vous pourriez être en mesure de:

  1. allouer de la page de mémoire
  2. l'écriture de données et le code machine à faire ce que vous voulez, l'appel de fonctions passé par pointeur etc.
  3. changement de protection de la mémoire de lecture/écriture lecture/exécution
  4. retourne le pointeur pour créer la fonction
  5. ne vous inquiétez pas que vous vous avez besoin de 4 ko par fonction

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

0voto

Bing Bang Points 314

Si 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())());
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X