C ++ полиморфизм - автоопределение производного типа - PullRequest
5 голосов
/ 06 августа 2010

У меня есть определенный код, который я хочу оптимизировать.Это выглядит так:

function abc( string format ) {
  if (format == "a") { // this is a string, I shouldn't have used single quote, sorry for the confusion
    classx::a t;
    doit(t);
  }
  if (format == "b"){
    classx::b t;
    doit(t);
  }
  if (format == "c"){
    classx::c t;
    doit(t) 
  }
  if (format == "d"){
    classx::d t; 
    doit(t);
  }
}

В настоящее время существует множество функций doit () с различными типами

function doit( classx:a ) {
   different code for a
}

function doit( classx:b ) {
   different code for b
}

... и т. Д.

Как вы можете видеть,много кода копируется.Однако я не могу понять, как уменьшить слова.Обратите внимание: doit (x) перегружен другим типом.Класс a, b, c, d является производным от класса с именем «X».

Я могу создать указатель типа classx :: X:

classx::X *t;
if (format == "a") t = new classx::a
if (format == "b") t = new classx::b
if (format == "c") t = new classx::c
if (format == "d") t = new classx::d
doit(*t)

, но тогда все равно нужно написать doit () для типа classx :: X с кучей «если тогда»и приведение к правильному типу ... поскольку C ++ не может автоматически определять и приводить к правильному типу.

Интересно, есть ли более быстрый / умный способ сделать это.Заранее спасибо.

Ответы [ 6 ]

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

Один из возможных подходов, который сводит повторение к добавлению новых записей в карту функций:

template<class T> void innerAbc() {
    T t;
    doit(t);
}

typedef std::map<std::string, void (*)()> FuncMap;

FuncMap initHandlers() {
    FuncMap m;
    m["a"] = &innerAbc<classx::a>;
    // ... extend here
    return m;
}   

void abc(const std::string& format) {
    static const FuncMap handlers = initHandlers();
    FuncMap::const_iterator it = handlers.find(format);
    if (it != handlers.end()) 
        it->second();
}
1 голос
/ 06 августа 2010

Простой шаблонный подход избавляет от большей части дублированного кода. Если вы хотите избежать серии операторов if, вы можете использовать карту или отсортированный вектор с двоичным поиском.

template<typename T> void forward_doit()
{
    T t;
    doit(t);
}

void func(string const& s)
{
    if (s == "a") return forward_doit<Classx::a>();
    if (s == "b") return forward_doit<Classx::b>();
    if (s == "c") return forward_doit<Classx::c>();
    // ...
}
1 голос
/ 06 августа 2010

Будет быстрее, если вы используете else if после первого if, чтобы он не продолжал тестирование после нахождения совпадения. Это также компактнее и проще для чтения ...

function abc(string format) {
    if (format == 'a')
        doit(classx::a());
    else if (format == 'b')
        doit(classx::b());
    else if (format == 'c')
        doit(classx::c())
    else if (format == 'd')
        doit(classx::d());
}
1 голос
/ 06 августа 2010

Поместите пары формат / конструктор в словарь. Ключом к этой строке формата является значение указателя на метод статической фабрики, который, по сути, является просто тонкой оболочкой над конструктором. Помимо простоты в обслуживании, он будет выполнять поиск по хешу или двоичный поиск, в зависимости от типа используемого словаря / карты.

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

с использованием форсированного препроцессора

#define MACRO(r, data, elem)                     \
if (format == '(elem)')  doit(classx::(elem)()); \
else

BOOST_PP_SEQ_FOR_EACH(MACRO, _, (a)(b)...) {
... // else condition
}

Я не уверен, как поместить макрос в '', однако: Как заключить аргумент в макрос в одну кавычку?

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

Вот подход с использованием макросов, который предполагает, что формат действительно является строкой. Одинарные кавычки, которые вы используете в оригинальном (Javascript?) Коде, предназначены для символов.

Я не могу работать с компактными шаблонами, да, иногда макросы все еще полезны!

#define FORMATTER(ltr) \
    if (format == #ltr) { \
    classx::##ltr t; \
    doit(t); \
  }

#define ELSEFORMATTER(ltr) else FORMATTER(ltr)

void abc( std::string format ) {
    FORMATTER(a)
    ELSEFORMATTER(b)
    ELSEFORMATTER(c)
    ELSEFORMATTER(d)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...