I combined the ideas from this answer and a comment to this question to create a new instance of a type extracted from a type list. The index of the desired type within the type list is provided at run time, only.
#include <iostream>
#include <memory>
class Base
{
public:
virtual void foo() = 0;
};
class A : public Base
{
public:
void foo() override
{
std::cout << "A" << std::endl;
}
};
class B : public Base
{
public:
void foo() override
{
std::cout << "B" << std::endl;
}
};
template <class... Types>
class type_list {};
using list_t = type_list<A,B>;
template <class... Types>
static constexpr std::size_t length = sizeof...(Types);
template <std::size_t idx, class... Types>
class extract
{
static_assert(idx < sizeof...(Types), "index out of bounds");
template <std::size_t i, std::size_t n, class... Rest>
struct extract_impl;
template <std::size_t i, std::size_t n, class T, class... Rest>
struct extract_impl<i, n, T, Rest...>
{
using type = typename extract_impl<i + 1, n, Rest...>::type;
};
template <std::size_t n, class T, class... Rest>
struct extract_impl<n, n, T, Rest...>
{
using type = T;
};
public:
using type = typename extract_impl<0, idx, Types...>::type;
};
template <std::size_t idx, class TypeList>
struct type_list_extract;
template <std::size_t idx, template <class...> class TypeList, class... Types>
struct type_list_extract<idx, TypeList<Types...>>
{
using type = typename extract<idx, Types...>::type;
};
template <std::size_t idx, class TypeList>
using type_list_extract_t = typename type_list_extract<idx, TypeList>::type;
template<std::size_t idx, bool done = false, class... Types>
struct action_wrapper
{
static std::unique_ptr<Base> get_instance_by_id(std::size_t id)
{
if (id == idx)
{
using type = type_list_extract_t<idx, Types...>;
return std::make_unique<type>();
}
static constexpr auto cont = length<Types...> < idx + 1;
return action_wrapper<idx + 1, cont, Types...>::get_instance_by_id(id);
}
};
template<std::size_t idx, class... Types>
struct action_wrapper<idx, true, Types...>
{
static std::unique_ptr<Base> get_instance_by_id(std::size_t id)
{
return nullptr;
}
};
static std::unique_ptr<Base> get_instance_by_id(std::size_t id)
{
return action_wrapper<0, false, list_t>::get_instance_by_id(id);
}
int main()
{
std::unique_ptr<Base> ptr = get_instance_by_id(1);
if (ptr)
{
ptr->foo();
}
}