2009-11-12 4 views
3

Je me demandais s'il y avait un moyen de transmettre des paramètres dynamiquement aux fonctions variadique. à savoir si j'ai une fonctionparamètres Passing dynamiquement aux fonctions variadique

int some_function (int a, int b, ...){/*blah*/} 

et j'accepte un tas de valeurs de l'utilisateur, je veux une certaine façon de passer ces valeurs dans la fonction:

some_function (a,b, val1,val2,...,valn) 

Je ne veux pas écrire différentes versions de toutes ces fonctions, mais je soupçonne qu'il n'y a pas d'autre option?

+1

si toutes les valeurs sont de même type (comme votre question semble impliquer - me corriger si je me trompe Je suggère de ne pas utiliser de fonction variadique et de passer un tableau à la place; voir ici pour un peu de magie de la macro pour le faire quand vous voulez passer un nombre fixe d'arguments: http://stackoverflow.com/questions/1375474/variable-arity-in-c/1375636#1375636 – Christoph

+0

Non, ils sont de différents types. – tommobh

+0

@tommobh: l'approche du tableau peut toujours fonctionner si vous enveloppez vos valeurs dans une union ou passez un tableau de 'void *' aux valeurs au lieu des valeurs elles-mêmes, mais ce sera moins élégant – Christoph

Répondre

1

Il pourrait être intéressant d'essayer juste de passer un tableau, puis utilisez les macros vararg de toute façon. Selon l'alignement de la pile, il se peut que cela fonctionne (tm).

Ceci est probablement pas une solution optimale, je principalement posté parce que je trouvais l'idée intéressante. Après l'avoir essayé, cette approche a fonctionné sur mon linux x86, mais pas sur x86-64 - elle peut probablement être améliorée. Cette méthode dépendra de l'alignement de la pile, de l'alignement de la structure et probablement plus.

void varprint(int count, ...) 
{ 
    va_list ap; 
    int32_t i; 

    va_start(ap, count); 
    while(count--) { 
     i = va_arg(ap, int32_t); 
     printf("Argument: %d\n", i); 
    } 
    va_end(ap); 
} 

struct intstack 
{ 
    int32_t pos[99]; 
}; 

int main(int argc, char** argv) 
{ 
    struct intstack *args = malloc(sizeof(struct intstack)); 
    args->pos[0] = 1; 
    args->pos[1] = 2; 
    args->pos[2] = 3; 
    args->pos[3] = 4; 
    args->pos[4] = 5; 

    varprint(5, *args); 
    return 0; 
} 
+0

cela ne fonctionnera pas car les tableaux ne sont pas passés en valeur, mais en tant que pointeurs – Christoph

+0

Vrai - quand j'ai réfléchi, j'ai pensé à pousser le tableau sur la pile. Mais C ne le fera pas si vous passez simplement le tableau à une fonction - vous devrez le faire vous-même. – gnud

+0

Lorsque vous utilisez une struct pour envelopper le tableau, il a la possibilité de travailler, au moins :) Voir l'exemple – gnud

8

fonctions VARIADIC utilisent une convention d'appel où l'appelant est responsable de popping les paramètres de la fonction de la pile, donc oui, il est possible de le faire dynamiquement. Il n'est pas standardisé en C et nécessiterait normalement un assemblage pour pousser manuellement les paramètres désirés, et invoquer correctement la fonction variadique. La convention d'appel cdecl exige que les arguments soient poussés dans le bon ordre, et après l'appel, les octets poussés en tant qu'arguments avant l'appel sont sautés. De cette façon, la fonction appelée peut recevoir un nombre arbitraire de paramètres, car l'appelant va gérer le retour du pointeur de la pile à son état de pré-appel. L'espace occupé par les arguments avant le ... est la limite inférieure sûre pour le nombre d'octets poussés. Des arguments variadiques supplémentaires sont interprétés lors de l'exécution.

FFCALL est une bibliothèque qui fournit des enveloppes pour passer les paramètres de façon dynamique à des fonctions variadique. Le groupe de fonctions qui vous intéresse est avcall. Voici un exemple d'appeler les fonctions que vous avez données ci-dessus:

#include <avcall.h> 

av_alist argList; 
int retVal; 
av_start_int(argList, some_function, retval); 
av_int(argList, a); 
av_int(argList, b); 
av_type(argList, val1); 
... 
av_type(argList, valn); 
av_call(argList); 

Vous trouverez peut-être aussi this link discuter générer des emballages autour des fonctions variadique en C, être d'intérêt pour justifier pourquoi ce ne fait pas partie de la norme C

+0

@Anacrolix Que voulez-vous dire "et invoquer l'appel varargs de manière appropriée."? La fonction s'occupe des varargs. – tommobh

+0

@tommobh: j'ai édité pour expliquer –

+0

@Anacrolix Merci, très apprécié. – tommobh

2

Une approche standard consiste à faire en sorte que chaque fonction variée soit accompagnée d'une contrepartie va_list (comme dans printf et vprintf). La version variadique convertit simplement ... en va_list (en utilisant des macros de stdarg.h) et appelle sa sœur va_list-taking, qui fonctionne réellement.

+0

Merci, je vais regarder dans cette approche. – tommobh

+0

@atzz Même problème cependant. J'ai encore besoin d'ajouter des paramètres dynamiquement à la fonction si elle appelle sa va_list-prise soeur ou non. Voulez-vous dire que je convertis les valeurs en va_list et que j'utilise la fonction soeur va_list-taking à la place? – tommobh

+0

@tommobh - Je suppose que j'ai mal interprété votre question. En lisant les commentaires, je comprends que vous avez un moyen d'obtenir des valeurs de différents types (par exemple de gui) et devez les passer à une fonction; est-ce correct? Dans ce cas, va_list n'aidera pas beaucoup. Si vous ne voulez pas compter sur les hacks, vous devrez changer les fonctions ... – atzz

0

Selon ce que vous êtes de passage autour, il pourrait être une union discriminée vous êtes après ici (comme laissé entrevoir dans les commentaires). Cela éviterait la nécessité de fonctions variadique ou des tableaux de void*, et répond à la question « comment peut-une_fonction savoir ce que vous avez réellement passé ce ».Vous pourriez avoir un code comme ceci:

enum thing_code { INTEGER, DOUBLE, LONG }; 

struct thing 
{ 
enum thing_code code; 
union 
{ 
    int a; 
    double b; 
    long c; 
}; 
}; 

void some_function(size_t n_things, struct thing *things) 
{ 
    /* ... for each thing ... */ 
    switch(things[i].code) 
    { 
     case INTEGER: 
     /* ... */ 
    } 
} 

Vous pouvez prendre un peu plus loin et d'éviter le commutateur en remplaçant le code avec un ou plusieurs pointeurs de fonctions qui font quelque chose d'utile à chaque thing. Par exemple, si ce que vous vouliez faire était de simplement imprimer chaque chose, vous pourriez avoir ceci:

struct thing 
{ 
void (*print)(struct thing*); 
union 
{ 
    ... 
}; 
} 

void some_function(size_t n_things, struct thing *things) 
{ 
    /* .. for each thing .. */ 
    things[i]->print(things[i]); 
    /* ... */ 
} 
+0

@Ned: La fonction variadique que j'utilise est dans une bibliothèque que je ne voudrais pas changer idéalement (ie je ne sais pas vouloir changer 'some_function()'). L'utilisateur peut entrer n'importe quelle quantité de n'importe quel type qu'il aime (dans des limites raisonnables), donc définir une union stricte ne semble pas fonctionner. Un tableau de void * ou la suggestion d'Anacrolix semble être mon meilleur pari. Soit cela ou j'écris un assemblage en ligne: S. – tommobh

+0

Mais si vous ne pouvez pas changer some_function, comment pouvez-vous lui passer un tableau de void *? Par utilisateur ici, parlons-nous d'un développeur utilisant votre code, ou d'un utilisateur final du programme? – Ned

+0

@Ned: Utilisateur = utilisateur final. Oui, je vois ce que tu veux dire en fait. Je ne veux pas changer la fonction parce que la fonction elle-même invoque une autre fonction variadique et que je devrais aussi changer cela. Ça ferait juste boule de neige. J'ai l'impression d'être de retour là où j'ai commencé. – tommobh

Questions connexes