1 votes

"Repasser les arguments d'une fonction

Je suis en train de faire ce devoir de classe, en utilisant le C classique, et je suis coincé avec ce problème sur les fonctions de rappel qui prennent les arguments variables count et type.

En gros, je travaille sur un arbre haché (un arbre où chacun des nœuds est un arbre de hachage), et j'ai une certaine stratégie de traversée qui sera utilisée plusieurs fois à des fins différentes, donc je l'ai implémentée comme suit ht_walk(HashTree tree, (*callback)(Element e)) de sorte que la fonction appelée en tant que callback traite l'élément de la manière nécessaire.

Le problème est que, dans la plupart des situations de mon problème, la fonction de rappel devra prendre différents arguments. Je sais comment concevoir une fonction avec une liste d'arguments variable en utilisant des fonctions 'variadiques' (en utilisant stdarg, printf-way), mais je ne sais pas comment 'repasser' ces arguments à la fonction de rappel.

Laissez-moi vous donner un exemple concret : supposons que j'ai une fonction de rappel appelée addToList(Element e, List list) et que ma déclaration ht_walk est maintenant ht_walk(HashTree tree, (*callback)(Element e), ...) . Considérons que je veux utiliser ht_walk comme dans le snippet suivant :

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

Y a-t-il un moyen de le faire ? Merci d'avance !

3voto

strager Points 41713

Vous pouvez résoudre ce problème en utilisant l'une des deux méthodes suivantes.

La méthode la plus courante, la plus compréhensible et la plus propre consiste à utiliser une structure "utilisateur" :

void ht_walk(HashTree tree, void (*callback)(Element e, void *user), void *user);

void addToList(Element e, void *arg)
{
    STATIC_ASSERT(sizeof(void *) >= sizeof(List));

    List list = arg;

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

Une autre méthode consiste à accepter va_list :

#include <stdarg.h>

void ht_walk(HashTree tree, void (*callback)(Element e, va_list args), ...)
{
    for(..)
    {
        va_list args;
        va_start(args, callback);
        callback(element, args);
        va_end(args);
    }
}

void addToList(Element e, va_list args)
{
    List list = va_arg(args, List);

    /* ... */
}

HashTree my_tree = ht_create();
/* run some algorithm that populates the tree somehow */
List my_list = list_create();
ht_walk(my_tree, addToList, my_list);

1voto

Uri Points 50687

Il existe une série de fonctions et de macros pour traiter les arguments variadiques (par exemple, va_start). GNU dispose d'une bon guide ici

Ceci étant dit, vous semblez décrire une sorte de modèle de visiteur. Je ne suis pas sûr d'aimer l'utilisation de listes d'arguments variadiques ici. Si vous ne pouvez pas établir les paramètres que votre callback doit recevoir de manière cohérente pour tous les nœuds, vous avez des problèmes pour commencer.

1voto

jeffamaphone Points 31732

Je ne pense pas que ce que vous voulez faire soit de passer un nombre arbitraire d'arguments. Je pense que ce que vous avez ici est un cas où vous avez plusieurs types de callback. Dans votre exemple, vous passez une liste, mais vous mentionnez également que vous passez parfois un élément. Donc, il me semble que ce que vous voulez vraiment faire est de déclarer votre callback pour prendre un void* et un drapeau pour indiquer ce qu'il est. Quelque chose comme :

void callback(void* arg, int type) {
    switch (type) {
    case ARG_TYPE_LIST:
        List* list = (List*)arg;
        ...
        break;
    case ARG_TYPE_ELEMENT:
        Element* ele = (Element*)arg;
        ...
        break;
    ...
}

Si vous passez les choses sous forme de liste, la liste devrait connaître le nombre, donc vous n'avez pas à vous soucier de passer cela.

Vous pouvez également définir plusieurs types de rappel et toujours rappeler le bon en fonction du type de traitement à effectuer.

Si vous n'êtes pas contraint de vous limiter au C (vous pouvez utiliser C++), vous pouvez envisager d'utiliser boost::bind car il est très utile pour ce genre de choses.

0voto

dmckee Points 50318

Je crois que vous utilisez le va_list macro. Voir la page de manuel de stdarg .

Cela fait un moment que je n'ai pas essayé...

0voto

FryGuy Points 5999

Je passerais dans un void *, car je ne pense pas que vous puissiez utiliser un nombre variable d'arguments dans une fonction de rappel. Mais je peux me tromper.

typedef void (*tree_walk_callback)(Element e, void *data);

add_element(Element e, void *data)
{
    List *list = (List *)data;
    add_element_to_list(list, e);
}

...in your function...
List my_list;
ht_walk(my_tree, &add_element, (void *)&my_list);

Si l'utilisateur veut passer plusieurs arguments, alors il passera une structure contenant ce dont il a besoin.

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