Универсальная генерация требуемого объекта класса по его версии
Я реализовал искусственный пример задачи, которую хотел бы реализовать:
#include <iostream>
#include <string>
using namespace std;
class Base
{
protected:
const int version;
Base(int ver) : version(ver) {}
public:
virtual void Deserialize(string json) = 0;
};
class A : public Base
{
int value;
public:
//VERSION = 1
A() : Base(1) {}
void Deserialize(string json) override { /*...*/ }
};
class B : public Base
{
float value;
public:
//VERSION = 16
B() : Base(16) {}
void Deserialize(string json) override { /*...*/ }
};
Base *create_obj(int version, string json)
{
Base* result;
switch (version)
{
case 1:
result = new A();
break;
case 16:
result = new B();
break;
default:
//ERROR
break;
}
return result;
}
int main()
{
string json = "example_json";
int want_version = 16;
Base* obj = create_obj(16, json);
}
Это работает, но когда добавляются новые классы с другими версиями, приходится каждый дополнять switch, что мне кажется, как-то костыльно. Есть ли способ реализовать эту задачу более профессионально?
Ответы (2 шт):
Как Вам вот такой способ
вначале добавим заготовку для шаблонной функции
template<int N>
Base* createX(string json) = delete;
теперь для каждого номера делаем специализацию
template<>
Base* createX<1>(string json) {
return new A();
}
template<>
Base* createX<16>(string json) {
return new B();
}
И вызываем теперь вот так
Base* obj = createX<1>(json);
Если передать неверный номер - оно не скомпилируется.
Если передать переменную - также не сможет. Ведь значение переменной доступно на этапе выполнения, а у нас этап компиляции. Но константу можно
const int want_version = 16;
Base* obj = createX<want_version>(json);
Но если просто не нравится сама функция create_obj, то как Вам такой вид:
#define CREATE(index, CLASS) case index:\
result = new CLASS();\
break;\
Base *create_obj(int version, string json)
{
Base* result;
switch (version)
{
CREATE(1,A)
CREATE(16,B)
default:
//ERROR
break;
}
return result;
}
#undef CREATE
Правда это макросы:)
Я бы предложил не повторять код, а написать всего один шаблон производного класса:
template <int VERSION>
class A : public Base
{
int value;
public:
//VERSION = 1
A() : Base(VERSION) {}
void Deserialize(string json) override { /*...*/ }
};
//если нужно, можете написать другое определение для конкретной версии
template <>
void A<16>::Deserialize(string json) {/*...*/ }
template <int version>
Base* create_obj(string json)
{
return new A<version>();
}
int main()
{
string json = "example_json";
constexpr int want_version = 16;
Base* obj = create_obj<want_version>(json);
}