Шаблонная фабрика, основанная на объединении различных значений перечислимого класса - PullRequest
0 голосов
/ 12 ноября 2018

Я пытаюсь создать вид Factory, основанный на некоторых значениях enum, которые пользователь может выбрать во время компиляции.

Главное здесь - создать своего рода магический переключатель, который работает с различными значениями более чем одного типа перечислений (тип объединения различных значений объединения, возможно, с одним и тем же значением).

enum class A  {A1, A2, A3, A4};
enum class B  {B1, B2, B3, B4, B6, B7};

struct ObjectBase 
{
virtual void apply(char input) = 0;
};

template<A a, class... Args>
struct Object : ObjectBase;

template<>
struct Object<A::A1, int> : ObjectBase
{
Object(int i) i_(i) { }
void apply(char input) { /*do stuff with i_*/}
int i_;
}

template<>
struct Object<A::A2, int, double> : ObjectBase
{
Object(int i, double d) i_(i), d_(d) { }
void apply(char input) { /*do stuff with i_*/}
int i_;
double d_;
}


template<class V, V value>
struct Element 
{
};

template<class V, V first, V last>
struct AllElementWithin 
{
};

Switcher< Element<A,A1>, Element<A,A2>, 
  AllElementWithin<B, B1, B3> > switcher (2, 4.0);
// This should create a switch / lookup table that 
// initializes for instance Object<A::A1> with 2
// and Object<A::A2> with 2 and 4

char myInput = 'F';
ObjectBase* ob = switcher.create(A::A1); 
// This should return an ObjectBase* that points to a 
// Object<A1,int>

ob->apply(myInput);

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

Лучше всего будет что-то, что компилируется с C ++ 11

[Редактировать] Дополнительная информация:

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

Пожалуйста, попросите другие разъяснения, если это не ясно

1 Ответ

0 голосов
/ 12 ноября 2018

Нечто подобное может удовлетворить ваши потребности (использование void * для того, чтобы не вводить базовый класс, оставляя вам настройку ...):

template <typename T, typename SelType, SelType SelValue, typename ... Arguments>
class Creator
{
    std::tuple<Arguments...> arguments;

    template<size_t ... Indices>
    void* create(std::index_sequence<Indices...>)
    {
        return new T( (std::get<Indices>(arguments), ...) );
    }

public:
    Creator(Arguments ... arguments)
        : arguments(arguments...)
    { }

    using SelectorType = SelType;
    static SelType const selectorValue = SelValue;
    void* create()
    {
        return create(std::index_sequence_for<Arguments...>{});
    }
};

template<typename ... Creators>
class Switcher
{
    std::tuple<Creators ...> creators;

    template<typename T, size_t Index>//, typename First, typename ... Remaining>
    void* select(T selector)
    {
        if constexpr(Index < sizeof...(Creators))
        {
            if constexpr(std::is_same<T, typename std::tuple_element<Index, decltype(creators)>::type::SelectorType>::value)
            {
                if(selector == std::tuple_element<Index, decltype(creators)>::type::selectorValue)
                    return std::get<Index>(creators).create();
            }
            return select<T, Index + 1>(selector);
        }
        return nullptr;
    }

public:
    Switcher(Creators ... creators)
            : creators(creators...)
    { }

    template <typename T>
    void* create(T t)
    {
        return select<T, 0U>(t);
    }
};

Использование:

Switcher switcher
(
        Creator<std::string, A, A::A1, char const*>("hello"),
        Creator<double, A, A::A2, double>(10.12),
        Creator<uint32_t, B, B::B1, unsigned int>(7U)
);

// just for demonstration purposes: don't care for the memory leaks...
std::cout << *(std::string*)switcher.create(A::A1) << std::endl;
std::cout << *(double*)switcher.create(A::A2) << std::endl;
std::cout << *(uint32_t*)switcher.create(B::B1) << std::endl;

На моей машине напечатано счастливо:

hello
10.12
7
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...