I want to be able to create switch statements over a type's ID. I've found a mechanism that could give a unique ID for different types. It's 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 to a constant that I could use as cases for the switch. But I get an error that the case expression is not a 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 it not a constant? How could I achieve this effect?
Edit:
Can I do something like this without the reinterpret_cast then?
I'm not sure it's a good idea but... just for fun... using the constexpr
counter, suggested in this page, you should be able to substitute the value of the pointer.
The following is a (I 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);
}
I would like to suggest another approach which involves 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 able to transform a string literal into a number, this is my approach but you can choose anoter function as long as it is constexpr
, then we create a macro which stringify the given parameter using the #
:
#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:
Cons:
TYPE_ID(I LOVE BACON)
is valid.TYPE_ID(size_t)
and TYPE_ID(unsigned long)
even if they might be the same type.constexpr
functions can not use reinterpret_cast
in any shape or form. Some more formal reading can be found at http://en.cppreference.com/w/cpp/language/constant_expression
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With