元函数将类型转换为整数,反之亦然

时间:2015-12-11 18:26:59

标签: c++ templates c++11 types template-meta-programming

typeid允许在运行时为每种类型分配唯一的std::type_index。我想做同样的事情,静态地使用两个元函数:

// Get a unique integral number associated with the provided type
template <class T>
struct encode_type
{
    using type = T;
    static constexpr std::size_t value = /* Metaprogramming magic */;
};

// Get the type uniquely associated with the provided value
template <std::size_t V>
struct decode_type
{
    static constexpr std::size_t value = V;
    using type = /* Metaprogramming magic */;
};

在C ++ 11中有没有办法做到这一点?

3 个答案:

答案 0 :(得分:8)

这是一个可行的解决方案,其中包括&#34;工作&#34;与GCC 5.2和Clang 3.7。

我使用Filip Roséen&#39; Constexpr Meta-Container进行了一些细微的修改。作为T.C.指出,将来这可能是made ill-formed,所以这个解决方案在生产代码中是完全不合理的,但现在它非常酷。我甚至不确定这是否符合100%标准。

// This is our meta-container
using TypeMap = atch::meta_list<class A>;

// Get a unique integral number associated with the provided type
template <class T>
struct encode_type
{
    using type = T;
    // Push T into the container and store the pre-push size
    //( requires slight change to Filip's code)
    static constexpr std::size_t value = TypeMap::push<T>();
};

// Get the type uniquely associated with the provided value
template <std::size_t V>
struct decode_type
{
    static constexpr std::size_t value = V;
    // Get the type at index V
    // (requires a small helper function addition)
    using type = decltype(TypeMap::at<V>());
};

我对原始代码所做的更改:

template<class T, class H = meta_list, std::size_t Size = counter::value()>
static constexpr std::size_t push (
  size_type = push_state<
    typename H::template value<>::template push<T>::result
  > ()
) { return Size; } 

我修改了atch::meta_list::push以在推送之前返回元容器的大小。我使用带有默认参数的模板参数来确保在推送之前计算大小。

template<size_type Idx, class H = meta_list>
static constexpr auto at () -> typename H::template value<>::template at<Idx>::result;

我在decltype中添加了一个小atch::meta_list辅助函数,以隐藏所有依赖名称的混乱。

一些测试代码:

int main () {
    std::array<int, 4> encoded { 
        encode_type<int>::value,
        encode_type<double>::value,
        encode_type<std::string>::value,
        encode_type<float>::value
    };

  std::cout << "Encoding: ";
  for (auto i : encoded) std::cout << i << ", ";
  std::cout << std::endl;

  std::array<std::type_index, 4> decoded {
      typeid(decode_type<0>::type),  
      typeid(decode_type<1>::type),
      typeid(decode_type<2>::type),
      typeid(decode_type<3>::type),
  };

  std::cout << "Decoding: ";
  for (auto i : decoded) std::cout << i.name() << ", ";
  std::cout << std::endl;
}

Clang和海湾合作委员会都发出了一系列警告,但他们俩都在工作&#34;!

Clang compiles, runs and outputs

  

编码:0,1,2,3,

     

解码:i,d,NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE,f,

GCC compiles, runs and outputs

  

编码:0,1,2,3,

     

解码:i,d,NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE,f,

也许你会在预处理步骤中做得更好......

答案 1 :(得分:4)

C ++中的类型不仅可以由size_t计算。

作为证据:

template<size_t N> struct bob {};

bob<X>类型的size_t类型与template<class...> struct alice {}; 值完全相同。

alice<bob<1>, bob<2>>

现在,alice<alice<bob<1>>是一种有效的类型。 alice也是如此。

bob种类型比size_t类型多。

因此,size_t值集合中没有注入到类型集合中。

因此,如果是双射的,任何这样的映射必须是不完整的。您只能将某些类型的子集映射到std::type_index并再返回。

你没有指定你只想处理一个类型的子集,所以答案是,你不能做你所要求的。

请注意,fwrite()只是一个可混合的,可比较的对象。它不是&#34;索引&#34;像一个整数值。

答案 2 :(得分:0)

我会给出一个部分解决方案,因为我对制作所有杂乱的细节不感兴趣。使用带有包含整个类型定义的参数的宏。现在使用该参数做三件事(需要辅助宏):

  • 使用参数as-is来定义类型。
  • 将字符串化的参数传递给加密哈希函数。
    • 该函数的输出将是某个位数的整数。毫无疑问,因为加密安全,该整数将大于内置的整数类型。因此,使用您自己的索引类型而不是type_idsize_t或诸如此类的东西;它可以是一个POD。
  • 该哈希函数的值是定义encode_typedecode_type的神奇值。
    • 由于模板参数中类型的限制,您可能必须使用包含哈希值的constexpr存储地址来定义decode_type

这里的基本原理与哥德尔证明他的不完备性定理相同,即一串字符总是有两种解释,第一种是正式系统的成员,第二种是数字。 (顺便提一下,证据的核心是创建一种从正式系统中讲述该数字的方式。)

可以肯定的是,使用C ++工具链中现成的工具的细节很混乱。但是,使关键元素成为可能的是字符串化运算符#。这类似于上面的第二种解释,即定义不是语言的成员,而是“只是数据”。