Универсальная генерация требуемого объекта класса по его версии

Я реализовал искусственный пример задачи, которую хотел бы реализовать:

#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 шт):

Автор решения: KoVadim

Как Вам вот такой способ

вначале добавим заготовку для шаблонной функции

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

Правда это макросы:)

→ Ссылка
Автор решения: AR Hovsepyan

Я бы предложил не повторять код, а написать всего один шаблон производного класса:

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);
}
→ Ссылка