0

I'm trying to create a kind of Factory based on some enum values that the user can choose a compile time.

The main thing here is to create a kind of magic switch that works on different values of more than one kind of enum (kind of union of different union values, possibly with same value).

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);

Is there an already kwown implementation pattern I can exploit here? I wouldn't like to re-invent the wheel.

Best would be something that compiles with C++11

[Edit] Some more info:

The factory should allow the creation of objects of different kind (that inherit from a specific base) in an efficient way. Ideally the user class that wants to add more objects can just create his enum and some classes that define the wanted behavior, and simply use the factory with these enums together with some other enums defined by other people.

Please ask for other clarifications if it's not clear

svoltron
  • 365
  • 1
  • 10

1 Answers1

2

Something like this might suit your needs (using void* for not having to introduce a base class, leaving adjustment to you...):

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);
    }
};

Usage:

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;

On my machine, printed happily:

hello
10.12
7
Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • yep, that seems a cool solution. I was looking for something in C++11, but at least this is an idea that works :) – svoltron Nov 12 '18 at 13:32
  • I've just noticed that this solution however does not fit exactly what I was looking for. Basically I would need something like Switcher switcher(myInputs); switcher(A::A1); switcher(A::A2); so, just one declaration that works for all the enums – svoltron Nov 12 '18 at 13:40
  • 1
    @svoltron It works for *any* arbitrary number of enums - note the `B::B1` being included in the demo; whichever enums you want to use, all is configured via the creator instantiations you provide as template arguments to the switcher... – Aconcagua Nov 12 '18 at 13:57
  • 1
    @svoltron I think the index sequence is the only C++14 feature I used. You might rebuild it yourself, there's more than one single answer for, e. g. [this one](https://stackoverflow.com/a/7858971/1312382). [Similar](https://stackoverflow.com/a/49672613/1312382), going a bit more into details. – Aconcagua Nov 12 '18 at 14:05
  • Yep, that's true, it works for any enum, my oversight. This solution works very well I think, it's only the basic usage I'm not totally ok with. What I meant was just to use a Switcher switcher(inputs); without defining anything else if possible, but I guess it's too much hehe. The index sequence it's ok, it's the C++17 constexpr if that I "don't like" – svoltron Nov 12 '18 at 14:18
  • 1
    @svoltron If you don't like the `if constexpr`, make an ordinary `if` from. I used it mainly for *assuring* that the if is resolved at compile time - but as the expression is constant anyway, chances are that the compiler will do so even without `constexpr`... – Aconcagua Nov 12 '18 at 17:12