C ++ constexpr values ​​for types

I want to be able to create switch statements on a type identifier. I found a mechanism that could give a unique identifier for different types. It is very simple:

template <typename T> struct type { static void id() { } }; template <typename T> constexpr const size_t type_id() { return reinterpret_cast<size_t>(&type<T>::id); } 

I thought this would evaluate a constant that I could use as cases for the switch. But I get an error that the case expression is not constant when I do the following:

 int main(void) { size_t a = type_id<int>(); switch (a) { case type_id<int>(): break; } return 0; } 

Why is this not a constant? How can I achieve this effect?

Edit:

Can I do something like this without reinterpret_cast?

+6
source share
4 answers

I'm not sure this is a good idea, but ... just for fun ... using the constexpr counter suggested on this page , you should substitute the pointer value.

The following (repeat: just for fun) full experiment

 #include <iostream> template <int N> struct flag { friend constexpr int adl_flag (flag<N>); }; template <int N> struct writer { friend constexpr int adl_flag (flag<N>) { return N; } static constexpr int value { N }; }; template <int N, int = adl_flag (flag<N> {})> int constexpr reader (int, flag<N>) { return N; } template <int N> int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {})) { return R; } int constexpr reader (float, flag<0>) { return 0; } template <int N = 1> int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value) { return R; } template <typename T> struct type { static constexpr int id { next() }; constexpr static int type_id () { return id; } }; void printType (int idT ) { switch ( idT ) { case type<int>::type_id(): std::cout << "- int type" << std::endl; break; case type<long>::id: std::cout << "- long type" << std::endl; break; default: std::cout << "- another type" << std::endl; break; } } int main () { int ii { type<int>::id }; int il { type<long>::type_id() }; printType(ii); printType(il); } 
+3
source

constexpr functions cannot use reinterpret_cast in any form or form. More formal readings can be found at http://en.cppreference.com/w/cpp/language/constant_expression

+3
source

This may solve your problem:

 #include <tuple> //Index from http://stackoverflow.com/a/18063608/3484570 template <class T, class Tuple> struct Index; template <class T, class... Types> struct Index<T, std::tuple<T, Types...>> { static const std::size_t value = 0; }; template <class T, class U, class... Types> struct Index<T, std::tuple<U, Types...>> { static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value; }; template <class T> constexpr std::size_t type_id() { //need to add every type in this tuple: return Index<T, std::tuple<int, double, char>>::value; } int main() { size_t a = type_id<int>(); switch (a) { case type_id<int>(): break; } } 

The good news is that you get a type_id<T>() , which can be used in the context of constexpr , for example, in case , as you like.
The bad news is that you need to list all supported types.
In practice, you can get used to the error message that occurs when you request the type_id unsupported type and just add it and ultimately add all the relevant types.

+2
source

I would like to suggest another approach that includes constexpr functions and macros (eeeewww ...):

 // Some naive text hashing function template <std::size_t SIZE> constexpr std::size_t hash(const char (&type_name)[SIZE]) { std::size_t result{0xf}; for (const auto &c : type_name) { result <<= 1; result |= c; } return result; } 

First, we create a constexpr function that can convert a string literal to a number, this is my approach, but you can choose anoter function, if it is constexpr , then we create a macro that builds this parameter using # :

 #define TYPE_ID(X) hash(#X) 

And now we can use it:

 int main(void) { size_t a = TYPE_ID(int); switch (a) { case TYPE_ID(int): break; } return 0; } 

Pros:

  • Pretty simple.
  • Tiny code.

Minuses:

  • Macros.
  • Takes any value, includes nonsense: TYPE_ID(I LOVE BACON) valid.
  • Sets a different result for TYPE_ID(size_t) and TYPE_ID(unsigned long) , even if they can be of the same type.
+2
source

Source: https://habr.com/ru/post/1015005/


All Articles