Reputation: 1346
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?
Upvotes: 6
Views: 4762
Reputation: 13298
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.Upvotes: 4
Reputation: 10001
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>()
that you can use in a constexpr
context such as in a case
like you wanted.
The bad news is that you need to list all supported types.
In practice you might get used to the error message that occurs when you ask for the type_id
of an unsupported type and just add it and eventually add all relevant types.
Upvotes: 3
Reputation: 66230
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);
}
Upvotes: 4
Reputation: 62603
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
Upvotes: 3