mrks mrks - 1 year ago 88
C++ Question

Choose template based on run-time string in C++

I have an attribute vector that can hold different types:

class base_attribute_vector; // no template args

template<typename T>
class raw_attribute_vector : public base_attribute_vector;

raw_attribute_vector<int> foo;
raw_attribute_vector<std::string> foo;

Based on run-time input for the type, I would like to create the appropriate data structure. Pseudocode:

std::string type("int");
raw_attribute_vector<type> foo;

Obviously, this fails. An easy, but ugly and unmaintainable workaround is a run-time switch/chained if:

base_attribute_vector *foo;
if(type == "int") foo = new raw_attribute_vector<int>;
else if(type == "string") ...

I read about run-time polymorphism with functors, but found it quite complex for a task that is conceptually easy.

What is the best and cleanest way to make this work? I played around with
, finding that while I can create a mapping from string to type, the lookup can only be done at compile time:

auto types =
hana::make_pair(BOOST_HANA_STRING("int"), hana::type_c<int>),
hana::make_pair(BOOST_HANA_STRING("string"), hana::type_c<std::string>)

All possible types are known at compile-time. Any suggestions are highly appreciated. In a perfect solution, I would create the name->type mapping in a single place. Afterwards, I would use it like this

std::vector<base_attribute_vector*> foo;

foo.push_back(magic::make_templated<raw_attribute_vector, "int">);
foo.push_back(magic::make_templated<raw_attribute_vector, "std::string">);



It is not required for this magic to happen at compile time. My goal is to have as readable code as possible.

Answer Source
enum class Type
    // ...

Type TypeFromString(const std::string& s)
    if (s == "int") { return Type::Int; }
    if (s == "string") { return Type::String; }
    // ...
    return Type::Unknown;

template <template <typename> class>
struct base_of;

template <template <typename> class C>
using base_of_t = typename base_of<C>::type;

And then the generic factory

template <template <typename> class C>
std::unique_ptr<base_of_t<C>> make_templated(const std::string& typeStr)
    Type type = TypeFromString(typeStr);
    const std::map<Type, std::function<std::unique_ptr<base_of_t<C>>()>> factory{
        {Type::Int, [] { return std::make_unique<C<int>>(); } },
        {Type::String, [] { return std::make_unique<C<std::string>>(); } },
        // ...
        {Type::Unknown, [] { return nullptr; } }

a specialization is needed for each base:

template <>
struct base_of<raw_attribute_vector> {
    using type = base_attribute_vector;

And then

auto p = make_templated<raw_attribute_vector>(s);


Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download