Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

compile time typeid for every type

Tags:

c++

c++14

I'd like a constexpr function that will return me a unique id for every C++ type, something like this:

using typeid_t = uintptr_t;

template <typename T>
constexpr typeid_t type_id() noexcept
{
  return typeid_t(type_id<T>);
}

int main()
{
  ::std::cout << ::std::integral_constant<typeid_t, type_id<float>()>{} << ::std::endl;

  return 0;
}

But I get an error:

t.cpp: In function 'int main()':
t.cpp:23:69: error: conversion from pointer type 'typeid_t (*)() noexcept {aka long unsigned int (*)() noexcept}' to arithmetic type 'typeid_t {aka long unsigned int}' in a constant-expression
   ::std::cout << ::std::integral_constant<typeid_t, type_id<float>()>{} << ::std::endl;
                                                                     ^
t.cpp:23:69: note: in template argument for type 'long unsigned int' 

Is there a workaround or another way?

like image 889
user1095108 Avatar asked Mar 20 '16 18:03

user1095108


2 Answers

You could use some tricks as shown in this answer.

There's even a library called ctti that utilizes the same trick, it should work out of the box

static_assert(ctti::type_id<int>() != ctti::type_id<float>(), "compile-time type-id comparison");

constexpr auto hash = ctti::type_id<int>().hash();
like image 107
Jamboree Avatar answered Oct 19 '22 17:10

Jamboree


Another way, this time involving a constexpr function, would be to use a well known hash function like in the following example (where I used the FNV v1a):

#include <cstdint>
#include <iostream>

static constexpr uint32_t offset = 2166136261u;
static constexpr uint32_t prime = 16777619u;

constexpr uint32_t helper(uint32_t partial, const char *str) {
    return str[0] == 0 ? partial : helper((partial^str[0])*prime, str+1);
}

constexpr uint32_t hash_str(const char *input) {
    return helper(offset, input);
}

struct MyClassA { static constexpr uint32_t type = hash_str("MyClassA"); };
struct MyClassB { static constexpr uint32_t type = hash_str("MyClassB"); };

int main() {
    std::cout << "MyClassA: " << MyClassA::type << std::endl;
    std::cout << "MyClassB: " << MyClassB::type << std::endl;
}

The drawbacks:

  • conflicts can happen
  • error-prone (at least, from my point of view)
  • pretty invasive a sollution

The main advantage is that you can use this solution if you need the types to be the same over different executions (as an example, if you have to store them somewhere and use them again after a while).

like image 20
skypjack Avatar answered Oct 19 '22 16:10

skypjack