質問

I know, there are several topic, asking very close things but I don't get it to work in my case.

I would like to build a templated factory with index access during runtime. Therefore I have several types with the same base type. The factory gets the types which it is able to procude per template parameters. The call to the factory just gives an index. This is a small example:

#include <iostream>
#include <memory>
#include <tuple>

struct Base {
};

struct A : Base {
  A(int) { std::cout << "A" << std::endl; }
};
struct B : Base {
  B(int) { std::cout << "B" << std::endl; }
};
struct C : Base {
  C(int) { std::cout << "C" << std::endl; }
};

template <typename ... Types>
struct Factory {
  typedef std::tuple<Types...> TypesTuple;

  std::shared_ptr<Base> operator ()(int index) {
    return produce(index);
  }

  std::shared_ptr<Base> produce(int index) {
    switch (index) {
    case 0: return std::make_shared<typename std::tuple_element<0, TypesTuple>::type>(42);
    case 1: return std::make_shared<typename std::tuple_element<1, TypesTuple>::type>(42);
    }
    throw; 
  }
};

//==============================================================================
int main() {    
  Factory<A, C> factory_ac;
  auto a1 = factory_ac(0);
  auto c1 = factory_ac(1);

  Factory<A, B, C> factory_bc;
  auto a2 = factory_bc(0);
  auto b2 = factory_bc(1);
  auto c2 = factory_bc(2);
}

I tried to overload the produce method with

template <typename = typename std::enable_if<std::tuple_size<TypesTuple>::value==2>::type>

counting up the size and providing the respective switch statements, but this does not compile, overload not allowed.

I tried using https://stackoverflow.com/a/7383493/2524462 but I couldn't get it to work, because the parameter packs don't expand with a lambda and wrapping it in a template function I get problems with the constexpr array, since I don't have trivial types.

Boost MPL for_eachcomes to mind, but I got problems compiling, because my types are not trivially constructable.

So how would one change the factory to get the main to compile and work?

役に立ちましたか?

解決

It seems this can be done quite straight forward:

template <typename T>
std::shared_ptr<Base> make() {
    return std::make_shared<T>();
}
template <typename... T>
class Factory {
public:
    std::shared_ptr<Base> operator()(int index) {
        static constexpr std::shared_ptr<Base> (*factories[])() = {
            &make<T>...
        };
        if (index < 0 && sizeof...(T) <= index) {
            throw std::range_error("type index out of range");
        }
        return (factories[index])();
    }
};

I'm currently not in the position to compile the code but something along this lines should work: the idea is to create an array of factory functions and just call into this array.

他のヒント

If I have understood your requirements correctly, I think this does what you want:

template<int... Is>
struct indices { typedef indices type; };

template<int N, int... Is>
struct make_indices : make_indices<N - 1, N - 1, Is...> { };

template<int... Is>
struct make_indices<0, Is...> : indices<Is...> { };

template<typename... Types>
struct Factory
{
    typedef std::tuple<Types...> TypesTuple;

    std::shared_ptr<Base> operator()(int const index)
    {
        return produce(index);
    }

    std::shared_ptr<Base> produce(int const index)
    {
        return produce_impl(make_indices<sizeof...(Types)>(), index);
    }

    template<int I, int... Is>
    std::shared_ptr<Base> produce_impl(indices<I, Is...>, int const index)
    {
        if (I == index) {
            return std::make_shared<typename std::tuple_element<I, TypesTuple>::type>(42);
        }

        return produce_impl(indices<Is...>(), index);
    }

    std::shared_ptr<Base> produce_impl(indices<>, int const index)
    {
        throw "Uh-oh!";
    }
};

See output here.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top