Come chiamare idiomaticamente le funzioni C++ in base al valore della variabile?
Domanda
Supponiamo di avere un tipo di dati enum TreeTypes { TallTree, ShortTree, MediumTree }
.
E devo inizializzare alcuni dati in base a un particolare tipo di albero.
Attualmente ho scritto questo codice:
int initialize(enum TreeTypes tree_type) {
if (tree_type == TallTree) {
init_tall_tree();
}
else if (tree_type == ShortTree) {
init_short_tree();
}
else if (tree_type == MediumTree) {
init_medium_tree();
}
return OK;
}
Ma questa è una specie di stupida ripetizione del codice.Non utilizzo nessuna delle potenti funzionalità C++ come i modelli.
Come potrei scrivere meglio questo codice?
Grazie, Boda Cydo.
Soluzione
Il tuo codice va bene per due o tre valori, ma hai ragione, hai bisogno di qualcosa di più potente a livello industriale quando ne hai centinaia.Due possibili soluzioni:
usa una gerarchia di classi, non enumerazioni: puoi quindi usare funzioni virtuali e fare in modo che il compilatore capisca quale funzione effettiva chiamare
crea una mappa di enum -> funzione, che inizializzi all'avvio: le tue chiamate di funzione diventano qualcosa di simile
map[enum]->func()
I modelli non funzionano così bene qui, perché stai cercando di prendere una decisione in fase di esecuzione, mentre i modelli fanno il loro lavoro in fase di compilazione.
Altri suggerimenti
In una parola: l'eredità
class Tree { public: virtual void initialize() = 0; }
class ShortTree : public Tree {
public:
virtual void initialize(){
/* Short Tree specific code here */
}
}
class MediumTree : public Tree {
public:
virtual void initialize(){
/* Medium Tree specific code here */
}
}
class TallTree : public Tree {
public:
virtual void initialize(){
/* Tall Tree specific code here */
}
}
Quindi, ovunque si desidera chiamare inizializzazione solo assicurarsi di avere correttamente un puntatore o un riferimento per il polimorfismo al lavoro:
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();
Utilizzare una tabella di ricerca che viene indicizzato dai valori enum (supponendo tutte le funzioni hanno la stessa firma), cioè:
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;
}
Prova un'istruzione switch:
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;
}
Se questa inizializzazione è davvero l'unica distinzione, quindi non sono sicuro di qualsiasi altro idioma migliorerebbe la situazione.
Si potrebbe ereditare da Albero e creare il giusto tipo di oggetto albero ... ma avresti ancora bisogno di differenziare quale per istanziare, quindi faresti ancora vento con un simile if / else blocco, da qualche parte.
Detto questo, se non v'è più di un semplice inizializzazione Sarebbe diverso, è necessario creare una sottoclasse e utilizzare le funzioni virtuali di emanare le differenze tra di loro.
E il modo in modello dal momento che avete indicato nel tag:
enum TreeTypes { Tall, Short, Medium };
struct TreeBase {
// (...)
};
struct TallTree : public TreeBase {
// (...)
};
struct ShortTree : public TreeBase {
// (...)
};
struct MediumTree : public TreeBase {
// (...)
};
template<TreeTypes N_type = Tall>
struct Tree : public TallTree {
// (...)
};
template<>
struct Tree<Short> : public ShortTree {
// (...)
};
template<>
struct Tree<Medium> : public MediumTree {
// (...)
};
In questo modo si ha classi separate per ciascun tipo di albero che può essere letta da puntatore base. avvolgendoli in classe Tree ti permette di fare questo:
Tree<Tall> tall_tree;
Tree<Short> short_tree;
Tree<Medium> medium_tree;