std::initializer_list value as template parameter - c++

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?

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"});
}

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.

Related

How to implement a template provding different values for different tyes

I am using a library which define various constants, like JQX_TYPE_INT, JQX_TYPE_LONG, JQX_TYPE_DOUBLE etc.
My problem is to relate values to c++ types.
For example, in order to have a function which works for various types i have to implement a copy of the same code, differing only in the constant, for every c++-type, e.g.:
myObj *createObject(int iSize, int iDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_INT);
return new myObj(iRealSize);
}
myObj *createObject(int iSize, double dDummy) {
int iRealSize = calcEffectiveSize(iSize, JQX_TYPE_DOUBLE);
return new myObj(iRealSize);
}
// ... same for float char, etc.
That is, apart from having to implement the same code several times, i have to use a dummy variable in order to use the constant related to the c++-type.
Is there some sort of template approach to do something like
template<tyepename T>
struct jqx_type {
static const int type;
// ... template magic
}
template<<typename T>
myObj *createObject(int iSize) {
int iRealSize = calcEffectiveSize(iSize, jqx_type<T>::type);
return new myObj(iRealSize);
}
I tried this
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
But this doesn't even compile.
Is there a way to achieve what i intend to do?
Here:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<typename T=int>
struct jqx_type {
static const int type = JQX_TYPE_INT;
};
template<typename T=long>
class jqx_types {
static const int type = JQX_TYPE_LONG;
};
You redeclared the same template several times with different default arguments. What you actually want is one template with different specializations:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
PS: type is very common name for member type aliases. I would not use it for an int. Maybe type_selector is a better name.
I would go with a much simpler
myObj *createObject(int iSize, int jqx_type) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject(16, JQX_TYPE_DOUBLE);
or if it needs to be constexpr
template <int jqx_type>
myObj *createObject(int iSize) {
return new myObj(calcEffectiveSize(iSize, jqx_type));
}
myObj *o = createObject<JQX_TYPE_DOUBLE>(16);
but lets assume you need the actual type it could be something like this:
template <typename T>
struct MyObj {
MyObj(std::size_t size, int jqx_type)
: iSize{calcEffectiveSize(size, jqx_type)} { ... }
MyObj * create(std::size_t size) {
constexpr int jqx_type = []() {
if constexpr (std::is_same_v<T, int>) return JQX_TYPE_INT;
if constexpr (std::is_same_v<T, double>) return JQX_TYPE_DOUBLE;
}();
return new MyObj{iSize, jqx_type};
}
};
or if you do want to go the type traits route then you have to specialize the template struct like this:
template<typename T>
struct jqx_type {
static const int type = JQX_TYPE_NONE;
};
template<>
struct jqx_type<int> {
static const int type = JQX_TYPE_INT;
};
template<>
struct jqx_types<long> {
static const int type = JQX_TYPE_LONG;
};
But that's as much code as specializing the createObject function.
PS: you can match and mix ideas, too. E.g. template createObject function with the if constexpr lambda.

pass char array properly to templated method

I have some class with enum variable and I wants to stringify that enum. For that reason I added typeValue with stringified values. Also I added separate class for getting stringified values but I can't pass variables in correct way.
Code like this:
#include <iostream>
#include <string>
#include <algorithm>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
inline static const char* typeValue[10] =
{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
}
Errors happened:
main.cpp:55:90: error: no matching function for call to ‘EnumStringify::getEnumTypeName(MyClass::TYPE, const char* [10])’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
^
main.cpp:45:21: note: candidate: template static std::string EnumStringify::getEnumTypeName(const EnumType&, const char (*)[10])
static std::string getEnumTypeName(const EnumType& t, const char (*typeValues)[10])
^~~~~~~~~~~~~~~
main.cpp:45:21: note: template argument deduction/substitution failed:
main.cpp:55:90: note: cannot convert ‘MyClass::typeValue’ (type ‘const char* [10]’) to type ‘const char (*)[10]’
std::cout << EnumStringify::getEnumTypeName<MyClass::TYPE>(a.type(), MyClass::typeValue).c_str();
Please help me to make it correct. Maybe with typedef would be better?
Two issues:
const char(*name)[] defines a pointer to an array of const chars, not strings.
MyClass::typeValue is of type const char* (&)[10] so it cannot be implicitly converted to a pointer to that array.
This works:
class MyClass{
//...
constexpr static const char* typeValue[10] = {
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
/**
* Get enum type string by value
*/
template <class EnumType>
static std::string getEnumTypeName(const EnumType& t, const char* const (&typeValues )[10])
{
std::string s(typeValues[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
I also added constexpr and pass the value by const reference since you do not want to change it.
My advice would to be use std::array, it alleviates all the issues with passing arrays around.
std::array solution
#include <iostream>
#include <string>
#include <algorithm>
#include <array>
#define stringify(name) #name
struct MyClass
{
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
MyClass(TYPE t)
:_type(t)
{
}
TYPE type()
{
return _type;
}
constexpr static auto typeValue = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
private:
TYPE _type;
};
struct EnumStringify
{
template <class EnumType, class Array>
static std::string getEnumTypeName(const EnumType& t, const Array& array)
{
std::string s(array[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
};
int main()
{
MyClass a(MyClass::TYPE::CLIENT);
std::cout << EnumStringify::getEnumTypeName(a.type(), MyClass::typeValue).c_str();
}
Template variable
If I were to write such code, I would use template variable which the user can specialize to provide strings for their own enums:
//
// "Stringify Library"
#define stringify(name) #name
struct MissingEnumTable{};
template<class Enum>
constexpr MissingEnumTable stringified_enum{};
template <class E>
static std::string getEnumTypeName(const E& t)
{
static_assert(!std::is_same_v<decltype(stringified_enum<E>),MissingEnumTable>,
"Enum E is missing stringified_enum table specialization.");
std::string s(stringified_enum<E>[static_cast<int>(t)]);
return (s.size()) ? s.substr(s.find_last_of(":") + 1) : "";
}
// END
// Library user
// - Provide strings for a custom array
enum class TYPE
{
UNKNOWN = 0,
CLIENT,
SERVER
};
template<>
constexpr auto stringified_enum<TYPE> = std::array{
stringify(TYPE::UNKNOWN),
stringify(TYPE::CLIENT),
stringify(TYPE::SERVER)
};
int main()
{
std::cout << getEnumTypeName(TYPE::UNKNOWN).c_str();
}

Fully specialized overloaded method is not called when used as template default parameter

I'd like to be able to specify default non-type template parameter by calling one of overloaded fuctions (that are template with full specialization). Following code represents the problem: I expecet FieldType2 getDefaultField<FieldType2>() to be printed, but Called getDefaultField() ! is printed.
#include <iostream>
enum class FieldType1 {
Description1,
Description2,
Description3
};
enum class FieldType2 {
Description1,
Description2,
Description3
};
template<class FiledType>
struct FieldDescription {
constexpr static int startPos{0};
constexpr static FieldType fieldType{};
};
struct ConcreteField2 : public FieldDescription<FieldType2> {};
template<class FieldType>
constexpr FieldType getDefaultField() {
return FieldType{};
};
template<>
constexpr FieldType1 getDefaultField<FieldType1>() {
return FieldType1::Description1;
};
template<>
constexpr FieldType2 getDefaultField<FieldType2>() {
return FieldType2::Description3;
};
template<class FieldDescr,
decltype(FieldDescr::fieldType) fieldType = getDefaultField<decltype(FieldDescr::fieldType)>()>
void process() {
if (fieldType == FieldType2::Description3) {
std::cout << "FieldType2 getDefaultField<FieldType2>()" << std::endl;
}
if (fieldType == FieldType2::Description1) {
std::cout << "Called getDefaultField() !" << std::endl;
}
}
int main() {
process<ConcreteField2>();
return 0;
}
decltype(FieldDescr::fieldType) is const FieldType2 not FieldType2. So your specialisation doesn't match and primary template is used.
You might use std::decay_t:
getDefaultField<std::decay_t<decltype(FieldDescr::fieldType)>>()
Demo
But as suggested in comment, that usage of dummy member is strange, a regular using type = FiledType; is more idiomatic.
I'd suggest don't use decltype and decay_t and use using to alias FieldType instead:
template<class FT>
struct FieldDescription {
using FieldType = FT;
constexpr static int startPos{0};
// constexpr static FieldType fieldType{}; // You can get rid of that member,
// unless used elsewhere
};
and
template <class FieldDescr,
typename FieldDescr::FieldType fieldType =
getDefaultField<typename FieldDescr::FieldType>()>

Converting Variadic template pack into std::initializer_list

Assume that there is a function which accepts several strings:
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
// do something
}
Now, I have a variadic template function say foo() as:
template<typename ...Args>
void foo () {
fun(???);
}
This method is called externally as:
foo<A, B, C, D>(); // where A, B, C, D are classes
And these classes which are passed as arguments are expected to contain a common static const member:
static const std::string value = "...";
Here are my questions (how to):
When inside foo(), check if all the Args contain value using
static_assert
Pass all such values to fun() to form an initializer_list; e.g.
fun({A::value, B::value, ...});
Searched several threads related to variadic templates and its unpacking but I am still novice in this area. Explanation in little more detail is much appreciated.
As for the second question, just do it this way:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
The mechanism is pretty intuitive: you create an initalizer list that contains the expanded Args::value pattern, thus resolving (in your case) to { A::value, B::value, C::value, D::value }.
Here is a complete program:
#include <string>
#include <iostream>
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
{
std::cout << s << " ";
}
}
template<typename ...Args>
void foo () {
fun({Args::value...});
}
struct A { static std::string value; };
struct B { static std::string value; };
struct C { static std::string value; };
struct D { static std::string value; };
std::string A::value = "Hello";
std::string B::value = "World";
std::string C::value = "of";
std::string D::value = "Variadic Templates";
int main()
{
foo<A, B, C, D>(); // where A, B, C, D are classes
}
And here is a live example.
As for the static assertion, you may write a type trait that determines whether a certain type has a member variable value:
template<typename T, typename V = bool>
struct has_value : std::false_type { };
template<typename T>
struct has_value<T,
typename std::enable_if<
!std::is_same<decltype(std::declval<T>().value), void>::value,
bool
>::type
> : std::true_type
{
typedef decltype(std::declval<T>().value) type;
};
Then, you could use it this way:
template<typename T>
struct check_has_value
{
static_assert(has_value<T>::value, "!");
};
template<typename ...Args>
void foo () {
auto l = { (check_has_value<Args>(), 0)... };
fun({Args::value...});
}
Here is a live example of a successful check (all classes has a value data member). Here is a live example of an unsuccessful check (class D's data member is called values)
The second part is easier:
template<typename ...Args>
void foo () {
fun({Args::value...});
}
The first part is tricky, because static_assert is a declaration, not an expression, so you'd have to expand the variadic pack within the first parameter. It may be easier just to let the call to fun do the checking for you. Here's a sketch of how to do it with an auxiliary all constexpr function:
constexpr bool all() { return true; }
template<typename... Args> constexpr bool all(bool first, Args&&... rest) {
return first && all(rest...);
}
template<typename ...Args>
void foo () {
static_assert(all(std::is_convertible<decltype(Args::value),
std::string>::value...), "All Args must have a value");
fun({Args::value...});
}
Here's an answer to both points:
#include <initializer_list>
#include <iostream>
#include <string>
#include <type_traits>
using namespace std;
void fun (const std::initializer_list<std::string>& strings) {
for(auto s : strings)
cout << s << endl;
}
// This uses SFINAE to find if there's a string T::value in T
template <typename T>
struct HasValue
{
typedef char OK; //sizeof() guaranteed 1
struct BAD { char x[2]; }; //sizeof() guaranteed >1
template <const string *>
struct Helper;
template <typename X>
static OK has(X*, Helper<&X::value>* = nullptr); //SF if &X::value is not a const string*
static BAD has(...); //will be picked in SF case
static const bool value = (sizeof(has((T*)nullptr)) == sizeof(OK));
};
// This template (and its specialisation) ensure all args have ::value
template <typename H, typename... T>
struct HaveValue : public integral_constant<bool, HasValue<H>::value && HaveValue<T...>::value>
{};
template <typename H>
struct HaveValue<H> : public HasValue<H>
{};
template <typename... Args>
void foo() {
static_assert(HaveValue<Args...>::value, "All arguments must have const string ::value");
fun({Args::value...}); //answer to point 2: create the initialiser list
}
// Example data follow
struct A
{
static const string value;
};
const string A::value = "AA";
struct B
{
static const string value;
};
const string B::value = "BB";
struct C{};
int main()
{
foo<A, B>();
//foo<A, B, C>(); //uncomment to have the static assertion fire
}
See it live.

Overriding return type in function template specialization

I would like to specialize a function template such that the return type changes depending on the type of the template argument.
class ReturnTypeSpecialization
{
public:
template<typename T>
T Item();
};
// Normally just return the template type
template<typename T>
T ReturnTypeSpecialization::Item() { ... }
// When a float is specified, return an int
// This doesn't work:
template<float>
int ReturnTypeSpecialization::Item() { ... }
Is this possible? I can't use C++11.
Since the specialization has to agree with the base template on the return type, you can make it so by adding a "return type trait", a struct you can specialize and draw the true return type from:
// in the normal case, just the identity
template<class T>
struct item_return{ typedef T type; };
template<class T>
typename item_return<T>::type item();
template<>
struct item_return<float>{ typedef int type; };
template<>
int item<float>();
Live example.
Note that you might want to stick to the following, so you only need to update the return-type in the item_return specialization.
template<>
item_return<float>::type foo<float>(){ ... }
// note: No `typename` needed, because `float` is not a dependent type
Do all of the specialization in a worker class and use a simple function as a wrapper that will be specialized implicitly.
#include <iostream>
using std::cout;
// worker class -- return a reference to the given value
template< typename V > struct worker
{
typedef V const & type;
static type get( V const & v ) { return v; }
};
// worker class specialization -- convert 'unsigned char' to 'int'
template<> struct worker<unsigned char>
{
typedef int type;
static type get( unsigned char const & v ) { return v; }
};
// mapper function
template< typename V > typename worker<V>::type mapper( V const & v )
{
return worker<V>::get(v);
}
int main()
{
char a='A';
unsigned char b='B';
cout << "a=" << mapper(a) << ", b=" << mapper(b) << "\n";
}
In this example, the specialization of unsigned char causes it to be converted to an int so that cout will display it as a number instead of as a character, generating the following output...
a=A, b=66
Perhaps you could use the following hack. Given these simple type traits:
template<bool b, typename T, typename U>
struct conditional { typedef T type; };
template<typename T, typename U>
struct conditional<false, T, U> { typedef U type; };
template<typename T, typename U>
struct is_same { static const bool value = false; };
template<typename T>
struct is_same<T, T> { static const bool value = true; };
You could write your class and specialized member function as follows:
class ReturnTypeSpecialization
{
public:
template<typename T>
typename conditional<is_same<T, float>::value, int, T>::type
Item();
};
// Normally just return the template type
template<typename T>
typename conditional<is_same<T, float>::value, int, T>::type
ReturnTypeSpecialization::Item() { return T(); }
// When a float is specified, return an int
template<>
int ReturnTypeSpecialization::Item<float>() { return 1.0f; }
Simple test program (uses C++11 just for verification):
int main()
{
ReturnTypeSpecialization obj;
static_assert(std::is_same<decltype(obj.Item<bool>()), bool>::value, "!");
static_assert(std::is_same<decltype(obj.Item<float>()), int>::value, "!");
}
Here is a live example.
You can do template specializations like so:
template<typename T>
T item() {
return T();
}
template<>
float item<float>() {
return 1.0f;
}
Hi I tried to use the template specialization for returning the parameter value for primitives as well as std::string data, while doing so I was getting lot of unresolved external, redefinition kind of errors.
so if any one face something like this, he/she can use something like below when want to return different data types including string,
NOTE: both the Template function must be the part of the Header file (*.h)...
so we are using template specialization string data type here...
inside class as a inline member we have to use template specialize method and in the same file we can define the template as well.
class ConfigFileParser
{
public:
bool ParseConfigFile(const std::string& file_name);
template <typename T>
T GetParameterValue(const std::string key);
template <>
std::string GetParameterValue<std::string>(const std::string key)
{
std::string param_val = "";
//do logical operation here...
return param_val;
}
private:
// private functions...
// private data...
};
template <typename T>
T ConfigFileParser::GetParameterValue(const std::string key)
{
T param_val = 0;
std::stringstream ss;
std::string val_str;
// do some operation here...
ss << val_str.c_str();
ss >> param_val;
return param_val;
}