Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 boost::hana, 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_map(
    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">);

foo[0]->insert(123);
foo[1]->insert("bla");

foo[0]->print();
foo[1]->print();

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

like image 338
mrks Avatar asked Jul 28 '16 18:07

mrks


1 Answers

Largely based on Jarod42's answer, this is what I will be using:

class base_attribute_vector {};

template<typename T>
class raw_attribute_vector : public base_attribute_vector {
public:
raw_attribute_vector() {std::cout << typeid(T).name() << std::endl; }
};

template<class base, template <typename> class impl>
base* magic(std::string type) {
    if(type == "int") return new impl<int>();
    else if(type == "float") return new impl<float>();
}

int main() {
    auto x = magic<base_attribute_vector, raw_attribute_vector>("int");
    auto y = magic<base_attribute_vector, raw_attribute_vector>("float");
}
like image 104
mrks Avatar answered Oct 15 '22 06:10

mrks