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.
I'd probably do something like this:
Features:
1 - time registration of objects by passing a named prototype
constant time lookup at runtime
lookup by any type which can be compared to
std::string
-
Short answer: no, you can't instruct the compiler to evaluate a runtime condition in compile time. Not even with hana.
Long answer: there are some (mostly language independent) patterns for this.
I'm assuming that your
base_attribute_vector
has somevirtual
method, most likelypure
, commonly called aninterface
in other languages.Which means that depending on the complexity of your real problem, you probably want a factory or an abstract factory.
You could create a factory or abstract factory without virtual methods in C++, and you could use hana for that. But the question is: is the added complexity really worth it for that (possibly really minor) performance gain?
(also if you want to eliminate every virtual call, even from
base_attribute_vector
, you have to make everything using that class a template, after the entry point where the switch happens)I mean, have you implemented this with virtual methods, and measured that the cost of the virtual calls is too significant?
Edit: another, but different solution could be using a variant type with visitors, like eggs::variant.
With
variant
, you can create classes with functions for each parameter type, and theapply
method will switch which function to run based on it's runtime type.Something like:
You can even use templated operators (possibly with enable_if/sfinae), if they have common parts.
You cannot do this. At best, you need to support a limited number of types, and switch between them using an
if
statement that can be evaluated at compile time.Largely based on Jarod42's answer, this is what I will be using:
I'd use an
std::map
that has strings as key andstd::function
as values. I would associate the string with a function that returns your type. Here's an example:Of course, this solution is valid if you only know the string value at runtime.
And then the generic factory
a specialization is needed for each base:
And then
Demo