Fonctions anonymes utilisant des expressions D'instruction GCC

Cette question n'est pas très spécifique; c'est vraiment pour mon propre enrichissement en C et j'espère que d'autres pourront La trouver utile aussi.

Avertissement: je sais que beaucoup auront l'impulsion de répondre avec "si vous essayez de faire FP, utilisez simplement un langage fonctionnel". Je travaille dans un environnement intégré qui doit être lié à de nombreuses autres bibliothèques C, et qui n'a pas beaucoup d'espace pour beaucoup plus de grandes bibliothèques partagées et ne supporte pas beaucoup d'runtimes linguistiques. En outre, l'allocation dynamique de la mémoire il est hors de question. Je suis aussi vraiment curieux.

Beaucoup d'entre nous ont vu cette macro c astucieuse pour les expressions lambda:

#define lambda(return_type, function_body) 
({ 
      return_type __fn__ function_body 
          __fn__; 
})

Et un exemple d'utilisation est:

int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; });
max(4, 5); // Example

En utilisant gcc -std=c89 -E test.c, le lambda se développe à:

int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; });

Donc, ce sont mes questions:

  1. Que déclare précisément la ligne int (*X);? Bien sûr, int * X; est un pointeur vers un entier, mais comment ces deux diffèrent-ils?

  2. Jeter un oeil à l'exapnded macro, que fait le final __fn__? Si j'écris une fonction de test void test() { printf("hello"); } test; - cela déclenche immédiatement une erreur. Je ne comprends pas cette syntaxe.

  3. Qu'est-ce que cela signifie pour le débogage? (Je prévois de m'expérimenter avec ceci et gdb, mais les expériences ou les opinions des autres seraient géniales). Est-ce que cela bousillerait les analyseurs statiques?

46
demandé sur royhowie 2012-05-02 02:45:35

4 réponses

Cette déclaration (à la portée du bloc):

int (*max)(int, int) =
    ({
    int __fn__ (int x, int y) { return x > y ? x : y; }
    __fn__;
    });

N'est pas C mais est valide GNU c.

Il utilise deux extensions gcc:

  1. fonctions imbriquées
  2. expressions d'instruction

Les Deux fonctions imbriquées (définition d'une fonction dans une instruction composée) et expressions d'instruction (({}), fondamentalement, un bloc qui donne une valeur) ne sont pas autorisés en C et proviennent de GNU C.

Dans une déclaration l'expression, la dernière expression est la valeur de la construction. C'est pourquoi la fonction imbriquée __fn__ apparaît comme une instruction d'expression à la fin de l'expression d'instruction. Un désignateur de fonction (__fn__ dans la dernière instruction d'expression) dans une expression est converti en un pointeur vers une fonction par les conversions habituelles. C'est la valeur utilisée pour initialiser le pointeur de fonction max.

37
répondu ouah 2012-05-01 23:16:35

Votre macro lambda exploite deux fonctionnalités géniales. Tout d'abord, il utilise des fonctions imbriquées pour réellement définir le corps de votre fonction (donc votre lambda n'est pas vraiment anonyme, il utilise juste une variable __fn__ implicite (qui devrait être renommée en autre chose, car les noms de soulignement double sont réservés au compilateur, donc peut-être que quelque chose comme yourapp__fn__ serait mieux).

Tout cela est lui-même effectué dans une instruction composée GCC (voir http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs), dont le format de base va quelque chose comme:

({ ...; retval; })

La dernière déclaration de l'instruction composée adresse de la fonction déclarée. Maintenant, int (*max)(int,int) reçoit simplement la valeur de l'instruction composée, qui est maintenant le pointeur vers la fonction 'anonyme' qui vient d'être déclarée.

Les macros de débogage sont une douleur royale bien sûr.

Quant à la raison pour laquelle test; .. au moins, ici, je obtenez le 'test redéclaré comme type de symbole différent', ce qui signifie que GCC le traite comme une déclaration et non comme une expression (inutile). Parce que les variables non typées par défaut à int et parce que vous avez déjà déclaré test en tant que fonction (essentiellement, void (*)(void)) vous obtenez cela.. mais je peux me tromper à ce sujet.

Ce n'est pas portable par aucun effort de l'imagination cependant.

5
répondu Mark Nunberg 2012-05-01 23:23:29
  1. int (*max)(int, int) est le type de variable que vous déclarez. Il est défini comme un pointeur de fonction nommé max qui renvoie int et prend deux ints comme paramètres.

  2. __fn__ fait référence au nom de la fonction, qui dans ce cas est max.

  3. Je n'ai pas de réponse. J'imagine que vous pouvez, si vous avez le lancer par le préprocesseur.

0
répondu gcochard 2012-05-01 22:50:18

Réponse partielle: Ce n'est pas int (*X) qui vous intéresse. Il est de type int (*X)(y,z). C'est un pointeur de fonction sur la fonction appelée X qui prend (y,z) et renvoie int.

Pour le débogage, ce sera vraiment difficile. La plupart des débogueurs ne peuvent pas tracer à travers une macro. Vous devrez probablement déboguer l'assemblage.

0
répondu Steve Rowe 2012-05-01 22:51:50