6
votes

Comment appeler idiomatiquement les fonctions C ++ en fonction de la valeur variable?

Supposons que j'ai un type de données Enum Treeypes {Talltree, Shorttree, MediumTree} .

Et je dois initialiser certaines données basées sur un type d'arborescence particulier.

Actuellement, j'ai écrit ce code: xxx

mais c'est une sorte de répétition de code stupide. Je n'utilise aucune des capacités puissantes C ++ telles que des modèles.

Comment puis-je écrire ce code mieux?

Merci, Boda Cydo.


7 commentaires

On dirait que l'amélioration de votre code devrait commencer à partir de la conception. Vous pouvez essayer de remplacer une bonne structure de contrôle ancienne comme si () {} else {} avec une utilisation intelligente d'autres caractéristiques de la langue, mais je doute qu'il améliorerait le code. Écrivez plus sur le problème lui-même. Qu'essayez-vous de modéliser?


Le code que j'utilise est plus complexe. J'ai essayé de le simplifier avec un très petit exemple. Je travaille sur un très grand projet et d'un seul endroit peut avoir divers types de données (ici je les ai appelés Talltree, Shorttree, MediumTree). Et pour chaque type de données, je dois faire une action. Dans mon cas, il est de 12 types différents, donc mon si / sinon si / sinon si / est 12 différent si / el / ... déclarations. Très mauvais.


Mais d'où vient l'argument de l'arbre_type lorsque vous appelez réellement initialiser? Initialise une fonction normale ou une méthode de certains cours? Est-ce que ces fonctions init agissent sur certaines données globales? Qu'est-ce qu'ils initient. Il n'y a pas de paramètres. Sont-ils des méthodes?


J'ai simplifié ça aussi. Ils prennent tous les 3 mêmes paramètres. L'argument Tree_Type provient de l'entrée de l'utilisateur, en fonction de l'utilisateur d'action choisissant. Une fois qu'il l'a choisie, je dois initialiser la boîte de dialogue pour lui (c'est ce que Tree_Type est en réalité - Type de dialogue), et la fonction d'initialisation prend le pointeur sur la fenêtre pour initialiser la boîte de dialogue sur, le pointeur sur les informations de l'utilisateur et la structure de données de journal ( Lorsque des actions précédentes sont conservées, il pourrait annuler des tâches).


Donc, ce que je pensais, était une fonction comme "initialisée" et je voulais template le paperacher sur arbre_type comme initialiser (fenêtre, user_info, enregistreur) et avoir des spécialisations individuelles telles que Modèle <> BOOL INITIALIZE (fenêtre * w, userinfo * u, enregistreur * l) mais cela ne fonctionne pas tout à fait. Je ne peux pas appeler modèle avec une variable arbores_type .


Oui. Pour utiliser des modèles comme cet arbre_type doit être connu au moment de la compilation. S'il vient de l'utilisateur, vous ne pouvez pas l'aider. Vous devez sélectionner la fonction en quelque sorte au moment de l'exécution. Il peut être cartographier comme le suggère de Neil, mais la carte doit être initialisée quelque part, de sorte que le type de liaison de code à fonction doit être écrit. Ce n'est pas bon s'il doit être écrit encore et encore. Si vous vous trouvez en train de l'écrire encore et encore quelque chose devrait être changé.


Je vais essayer la suggestion de Rémy Lebeau d'utiliser une matrice (carte) des pointeurs de fonction. (comme Neil suggère)


6 Réponses :


18
votes

Votre code est correct pour deux ou trois valeurs, mais vous avez raison, vous avez besoin de quelque chose de plus de force industrielle lorsque vous en avez des centaines. Deux solutions possibles:

  • Utilisez une hiérarchie de classe, pas enums - Vous pouvez ensuite utiliser des fonctions virtuelles et que le compilateur fonctionne de la fonction réelle à appeler

  • Créez une carte de Enum -> Fonction, que vous initialisez au démarrage - Vos appels de fonction deviennent quelque chose comme mappe [ENUM] -> FUNC ()

    Les modèles ne fonctionnent pas si bien ici, car vous essayez de prendre une décision au moment de l'exécution, tandis que les modèles font leurs affaires à la compilation.


5 commentaires

La carte fonction peut bénéficier de l'utilisation de boost :: fonction / boost :: Bind () ou les équivalents TR1.


Cela ressemble à un circuit équitable, C ++ a déjà ceci avec des fonctions virtuelles et du polymorphisme ...


@josh Golly, n'est-ce pas ce que dit ma réponse?


@Neil désolé, mal interprété; Je suppose que mon œil a été attiré par la partie [Enum].


S'il y a des centaines de valeurs, une première idée signifie des centaines de sous-classes; Est-ce vraiment une bonne idée?



0
votes

Essayez un relevé d'interrupteur:

int initialize(enum TreeTypes tree_type) {
    switch (tree_type) {
        case TallTree: 
            init_tall_tree();
            break;
        case ShortTree:
            init_short_tree();
            break;
        case MediumTree:
            init_medium_tree();
            break;
    }
    return OK;
}


0 commentaires

0
votes

Si cette initialisation est vraiment la seule distinction, je ne suis pas sûr que tout autre idiome améliorerait la situation.

Vous pouvez sous-classe d'arborescence et créer la bonne sorte d'objet d'arbre ... mais vous devez toujours différencier lequel à instancier, de sorte que vous vous retrouvez toujours avec un bloc similaire si / sinon, quelque part. < / p>

Cela dit, s'il ya plus qu'une initialisation que celle qui soit différente, vous devez sous-classer et utiliser des fonctions virtuelles pour édicter les différences entre elles.


0 commentaires

8
votes

en un mot: héritage xxx pré>

puis où que vous souhaitiez appeler initialiser, assurez-vous d'avoir un pointeur ou une référence pour le polymorphisme pour fonctionner correctement: P>

Vector<Tree*> trees;
trees.push_back(new SmallTree());
trees.push_back(new MediumTree();
trees.push_back(new TallTree();

// This will call the tree specific code for each tree in the vector
for(vector<Tree*>::iterator tree = trees.begin(); tree!=trees.end(); ++tree)
    tree->initialize();


2 commentaires

Votre classe de base avait mieux d'avoir un destructeur virtuel, en supposant que vous appelez jamais supprimer sur les choses que vous avez nouvelles.


@Neil Butterworth mon code a été pris et géré avec ... je ne le reconnais plus :)



2
votes

Utilisez une table de recherche indexée par les valeurs ENUM (en supposant que toutes les fonctions ont la même signature), à ​​savoir:

enum TreeTypes { TallTree, ShortTree, MediumTree, MaxTreeTypes }

typedef void (*p_init_func)(void); 

p_init_func initialize_funcs[MaxTreeTypes] =
{
    &init_tall_tree, 
    &init_short_tree,
    &init_medium_tree
};

int initialize(enum TreeTypes tree_type)
{ 
    initialize_funcs[tree_type]();
    return OK; 
} 


0 commentaires

1
votes

Et le modèle de modèle puisque vous l'avez signalé dans vos balises: xxx

de cette façon, vous avez des classes séparées pour chaque type d'arborescence accessible par le pointeur de base. En l'enveloppant dans la classe d'arbres, laissez-le faire ceci: xxx


0 commentaires