compile time typeid for every type

会有一股神秘感。 提交于 2020-01-23 07:20:32

问题


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?


回答1:


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();



回答2:


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).




回答3:


This's not a constexpr function, but if there is not the constraint for the types to be persistent over multiple executions, you can use CRTP idiom as an alternative approach to achieve the same result.
It follows a minimal, working example:

#include <cstddef>
#include <iostream>

struct BaseClass {
protected:
    static std::size_t next() noexcept {
        static std::size_t counter = 0;
        return counter++;
    }
};

template<class D>
struct Base: public BaseClass {
    static std::size_t type() noexcept {
        static std::size_t type_ = BaseClass::next();
        return type_;
    }
};

struct A: public Base<A> { };
struct B: public Base<B> { };

int main() {
    std::cout << A::type() << std::endl;
    std::cout << B::type() << std::endl;
}

This way you have a base class from which to derive for all those types for which you would like to have an unique identifier.



来源:https://stackoverflow.com/questions/36117884/compile-time-typeid-for-every-type

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!