Как идиоматически вызовите функции C ++ на основе значения переменного?
Вопрос
Предположим, у меня есть тип данных enum TreeTypes { TallTree, ShortTree, MediumTree }
.
И я должен инициализировать некоторые данные, основанные на одном конкретном типе дерева.
В настоящее время я написал этот код:
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;
}
Но это какой-то тупые повторение кода. Я не использую никаких мощных возможностей C ++, таких как шаблоны.
Как я могу написать этот код лучше?
Спасибо, Бода Cydo.
Решение
Ваш код в порядке на два или три значения, но вы правы, вам нужна что-то более промышленная сила, когда у вас есть сотни из них. Два возможных решения:
Используйте классовую иерархию, а не Enums - затем вы можете использовать виртуальные функции и иметь работу компилятора, какую фактическую функцию звонить
Создать карту Enum -> Функция, которую вы инициализируете при запуске - вызовы функций, затем стали чем-то вроде
map[enum]->func()
Шаблоны здесь не работают так хорошо, потому что вы пытаетесь принять решение во время выполнения, тогда как шаблоны делают свои вещи при компиляции времени.
Другие советы
Одним словом: наследство
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 */
}
}
Тогда везде, где вы хотите позвонить инициализировать, просто убедитесь, что указка или ссылка на полиморфизм, чтобы правильно работать:
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();
Используйте таблицу поиска, которая индексируется значениями Enum (при условии, что все функции имеют одинаковую подпись), т. Е.
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;
}
Попробуйте оператор коммутатора:
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;
}
Если эта инициализация действительно является единственным различием, то я не уверен, что какая-либо другая идиома улучшит ситуацию.
Вы можете подкласс из дерева и создать правильный вид предмета дерева ... но вам все равно нужно дифференцировать, какой из них можно создать, поэтому вы все равно должны быть подобными, если бы / остальные блокировки, где-то.
Тем не менее, если есть больше, чем просто инициализация, которая отличается, вам следует подкласс и использовать виртуальные функции для принятия различий между ними.
И шаблон, так как вы указали это в свои теги:
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 {
// (...)
};
Таким образом, у вас есть отдельные классы для каждого типа дерева, который можно добраться до базового указателя. Удавая их в класс деревьев, позвольте вам сделать это:
Tree<Tall> tall_tree;
Tree<Short> short_tree;
Tree<Medium> medium_tree;