Как идиоматически вызывать функции C ++ на основе значения переменной? - PullRequest
3 голосов
/ 06 августа 2010

Предположим, у меня есть тип данных 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 ++, такие как шаблоны.

Как мне лучше написать этот код?

Спасибо, Бода Чидо.

Ответы [ 6 ]

16 голосов
/ 06 августа 2010

Ваш код подходит для двух или трех значений, но вы правы, вам нужно что-то более мощное, когда у вас их сотни. Два возможных решения:

  • используйте иерархию классов, а не перечисления - вы можете использовать виртуальные функции и заставить компилятор определить, какую именно функцию вызывать

  • создайте карту enum -> function, которую вы инициализируете при запуске - ваши вызовы функций затем становятся чем-то вроде map[enum]->func()

Шаблоны здесь работают не очень хорошо, потому что вы пытаетесь принять решение во время выполнения, тогда как шаблоны делают свое дело во время компиляции.

7 голосов
/ 06 августа 2010

Одним словом: наследство

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();
1 голос
/ 06 августа 2010

Используйте таблицу поиска, которая проиндексирована значениями перечисления (при условии, что все функции имеют одинаковую подпись), то есть:

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 голосов
/ 06 августа 2010

И шаблон с тех пор, как вы указали его в своих тегах:

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 позволит вам сделать это:

Tree<Tall> tall_tree;
Tree<Short> short_tree;
Tree<Medium> medium_tree;
0 голосов
/ 06 августа 2010

Если эта инициализация действительно является единственным отличием, то я не уверен, что любая другая идиома могла бы улучшить ситуацию.

Вы можете создать подкласс из дерева и создать правильный вид объекта дерева ... но вам все равно нужно будет различать, какой из них создавать, так что вы все равно получите подобный блок if / else где-нибудь. 1003 *

Тем не менее, если есть нечто большее, чем просто отличная инициализация, вы должны создать подкласс и использовать виртуальные функции, чтобы определить различия между ними.

0 голосов
/ 06 августа 2010

Попробуйте оператор 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;
}
...