2015-07-07 3 views
0

Мне интересно узнать больше о преимуществах и ограничениях RTTI на C++. Предположим, у меня есть следующий сценарий:C++ RTTI Registry Pattern

class C {}; 
class C0 : public C {}; 
class C1 : public C {}; 
... 

void print(int id, C* c) { 
    if (id == 0) dynamic_cast<C0 *>(c)->print(); 
    else if (id == 1) dynamic_cast<C0 *>(c)->print(); 
    ... 
} 

Возможно ли реализовать описанный выше пример с использованием шаблона реестра? Например, используя что-то вроде этого:

map<int, ?> registry; 
void print(int id, C* c) { 
    registry[id](c)->print(); 
} 
+0

Вы хотите два свободно стоящих 'print' функций с тем же типом подписи и возврата, но называется по-разному? – AndyG

+0

Ваш пример показывает полиморфизм и виртуальную отправку, это не имеет никакого отношения к RTTI. – Nax

+0

Возможно, вы захотите изменить второй 'dynamic_cast' на' C1 * '. – Quentin

ответ

1

При использовании полиморфизма и виртуальный метод представляется более целесообразным, вы можете использовать что-то вроде следующего для регистрации и отправки в соответствии с id

class PrintCaller 
{ 
public: 
    template <typename T> 
    std::size_t register_class() 
    { 
     m.push_back([](C* c) { 
      auto* p = dynamic_cast<T*>(c); 
      if (p) { 
       p->print(); 
      } else { 
       throw std::runtime_error("Incorrect type"); 
      } 
     }); 
     return m.size() - 1; 
    } 

    void print(std::size_t id, C* c) const { 
     if (id < m.size()) { 
      m[id](c); 
     } else { 
      throw std::runtime_error("invalid id"); 
     } 
    } 

private: 
    std::vector<std::function<void(C*)>> m; 
}; 

Live example

3

Это было бы легче всего решить, просто делая print виртуальную функцию. Тогда вы могли бы просто:

void print(C* c) 
{ 
    c->print(); 
} 

, и это будет полезно для всех производных классов.

Но если вы хотите сохранить print не виртуальным, тогда, как вы понимаете, есть вопрос, как что-то вроде registry[id](c)->print(); может работать. Тип значения карты является фактом компиляции, но вы хотите разницу во времени выполнения.

Ну, я могу думать об одном способе сделать это ... используя виртуальные функции. Вам нужно будет создать класс, который работает как обертка для C с производными версиями, которые соответствуют типам, полученным из C. Использование некоторых шаблонов, вероятно, может сделать это несколько приличным для решения. Затем карта объявляется в терминах указателей на базу, но заполняется через производные оболочки.

Но в конце концов, это требует гораздо более сложной работы и не дает больше пользы, чем могло бы быть достигнуто, просто сделав фактически виртуальным в действительности print.

+0

Проблема, с которой я столкнулся, заключается в том, что необходимо использовать шаблонную функцию 'print()' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '), чтобы он не мог быть виртуальным. Как выглядит производная версия оболочки для 'C'? – agg212

+1

Ну, это скорее меняет сценарий, так как параметр шаблона является еще одним фактором времени компиляции. Где бы ваша оригинальная реализация 'print (int id, C * c)' получала разные типы 'T' для своих разных ветвей if/else? Мне было бы интересно, может быть, что этот шаблон может быть реорганизован. Но по мере того, как ваш вопрос стоит прямо сейчас, вы, возможно, фактически урезали слишком много деталей своей реальной ситуации, чтобы получить эффективное решение для этого. – TheUndeadFish