Reputation: 39638
I am trying to do something like this:
template<typename enumType,
std::initializer_list<enumType> values,
std::initializer_list<std::string> mappings>
struct enum_converter {
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
};
I want to use it as follows:
enum test_enum {value_a, value_b};
struct test_enum_converter : public enum_converter<
test_enum,
{value_a, value_b},
{"a", "b"}> {};
GCC tells me:
class std::initializer_list<_Tp> is not a valid type
for a template constant parameter.
Adding const
to the type does not change anything. Is there a workaround or a similar solution?
Upvotes: 5
Views: 2137
Reputation: 171097
Only integral types, enumerations, pointers and references are allowed as non-type template parameters. std::initializer_list
is neither of those.
Do you need the values & mappings to be template parameters? How about making them normal parameters of the constructor, and keeping only the enum as a template param?
template<typename enumType>
struct enum_converter {
enum_converter(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings) : values(values), mappings(mappings)
enumType toEnum(const std::string& literal) { ... }
std::string toString(const enumType value) { ... }
private:
std::initializer_list<enumType> values;
std::initializer_list<std::string> mappings;
};
enum_converter<test_enum> test_enum_converter({...}, {...});
int main()
{
test_enum_converter.toEnum("bla");
}
EDIT
Here is an instance-free alternative:
template<typename enumType>
struct enum_converter {
static init(std::initializer_list<enumType> values, std::initializer_list<std::string> mappings)
{ s_values = values; s_mappings = mappings; }
static enumType toEnum(const std::string& literal) { ... }
static std::string toString(const enumType value) { ... }
private:
static std::initializer_list<enumType> s_values;
static std::initializer_list<std::string> s_mappings;
};
Just call init()
once before using the class.
If you need more than one instantiation for a particular enumeration, you can add a disambiguation parameter to the template, like this:
template <typename enumType, typename tag>
struct enum_converter { /*as before*/ };
int main()
{
struct data_strings;
struct user_readable_strings;
enum_converter<test_enum, data_strings>::init({...}, {"a", "b"});
enum_converter<test_enum, user_readable_strings>::init({...}, {"Option A", "Option B"});
}
Upvotes: 3
Reputation: 476940
Pointers can only be template arguments if they are constant expressions, i.e. the address of an object with extern linkage. The following code works, though it's your call whether it's worth it:
typedef char const * charptype;
template <int, charptype> struct item;
template <typename ...> struct econv
{
static constexpr charptype convert(int) { return nullptr; }
};
template <int I, charptype S, typename ...Tail>
struct econv<item<I, S>, Tail...>
{
static constexpr charptype convert(int i)
{
return i == I ? S : econv<Tail...>::convert(i);
}
};
#include <iostream>
extern char const Hello[] = "Hello";
extern char const World[] = "World";
int main()
{
std::cout << econv< item<1, Hello>
, item<2, World> >::convert(2)
<< "\n";
}
By putting the strings into an anonymous namespace and using some helper macros, this approach might be cleaned up a bit to look presentable.
Upvotes: 1