模板元程序将类型转换为唯一编号

时间:2009-11-10 14:43:10

标签: c++ templates metaprogramming

我刚开始玩元编程,我正在研究不同的任务,只是为了探索这个领域。其中之一是生成一个唯一的整数并将其映射到类型,如下所示:

int myInt = TypeInt<AClass>::value;

其中value应该是编译时常量,而后者可以在元程序中进一步使用。

我想知道这是否可能,在这种情况下如何。因为虽然我已经学到了很多关于探索这个主题的知识,但我还是没有找到答案。

(P.S.A是/否答案比不使用元编程的c ++解决方案更令人满意,因为这是我正在探索的领域)

9 个答案:

答案 0 :(得分:7)

原则上,这是可能的,虽然解决方案可能不是您正在寻找的。

简而言之,您需要提供从类型到整数值的显式映射,每种可能的类型都有一个条目:

template< typename T >
struct type2int
{
   // enum { result = 0 }; // do this if you want a fallback value
};

template<> struct type2int<AClass> { enum { result = 1 }; };
template<> struct type2int<BClass> { enum { result = 2 }; };
template<> struct type2int<CClass> { enum { result = 3 }; };

const int i = type2int<T>::result;

如果您没有在基本模板中提供回退实现,那么对于T的未知类型,这将失败,否则它将返回回退值。

根据您的具体情况,可能还有其他可能性。例如,您可以在类型本身内定义这些数字:

class AClass {
  public:
    enum { inta_val = 1 };
  // ...
};

class BClass {
  public:
    enum { inta_val = 2 };
  // ...
};

// ... 

template< typename T >
struct type2int
{
   enum { result = T::int_val }; // will fail for types without int_val
};

如果您提供更多上下文,也可能有其他解决方案。

编辑

  

实际上没有任何更多的背景。我正在研究它是否真的可行,但没有自己分配数字。

我认为迈克的排序理念是一种很好的方法(再次,对于一组固定的类型),而不必明确地指定数字:它们是由排序隐式给出的。但是,我认为使用类型列表会更容易。列表中任何类型的索引都是其编号。我认为以下内容可能会这样:

// basic type list manipulation stuff
template< typename T1, typename T2, typename T3...>
struct type_list;

// meta function, List is assumed to be some instance of type_list
template< typename T, class List >
struct index_of {
  enum { result = /* find index of T in List */ };
};

// the list of types you support
typedef type_list<AClass, BClass, CClass> the_type_list;

// your meta function
template< typename T >
struct type2int
{
   enum { result = index_of<T, the_type_list>::result };
};

答案 1 :(得分:7)

这就是你想要的。根据需要分配值。它利用了函数中静态的分配方式。

inline size_t next_value()
{
     static size_t id = 0;
     size_t result = id;
     ++id;
     return result;
}

/** Returns a small value which identifies the type.
    Multiple calls with the same type return the same value. */
template <typename T>
size_t get_unique_int()
{
     static size_t id = next_value();
     return id;
}

这不是关于类固醇的模板元编程,但我认为这是一件好事(相信我!)

答案 2 :(得分:6)

与Michael Anderson的方法类似,但此实现完全符合标准,可以在编译时执行。从C ++ 17开始,看起来constexpr值将被允许用作其他模板元编程目的的模板参数。此外,unique_id_type可与==,!=,&gt;,&lt;等进行比较,以便进行排序。

// the type used to uniquely identify a list of template types
typedef void (*unique_id_type)();

// each instantiation of this template has its own static dummy function. The
// address of this function is used to uniquely identify the list of types
template <typename... Arguments>
struct IdGen {
   static constexpr inline unique_id_type get_unique_id()
   {
      return &IdGen::dummy;
   }

private:
   static void dummy(){};
};

答案 3 :(得分:3)

我到目前为止最接近的是能够保持一个类型列表,同时跟踪返回基地的距离(给出一个唯一值)。请注意,如果您正确跟踪事物,此处的“位置”将是您的类型所独有的(请参阅示例的主要内容)

template <class Prev, class This>
class TypeList
{
public:
   enum
   {
      position = (Prev::position) + 1,
   };
};

template <>
class TypeList<void, void>
{
public:
  enum
  {
     position = 0,
  };
};


#include <iostream>

int main()
{
        typedef TypeList< void, void> base;  // base
        typedef TypeList< base, double> t2;  // position is unique id for double
        typedef TypeList< t2, char > t3; // position is unique id for char

        std::cout << "T1 Posn: " << base::position << std::endl;
        std::cout << "T2 Posn: " << t2::position << std::endl;
        std::cout << "T3 Posn: " << t3::position << std::endl;

}

这很有效,但我自然不想以某种方式指定“prev”类型。最好找出一种自动跟踪的方法。也许我会更多地玩它,看看它是否可能。绝对是一个有趣/有趣的谜题。

答案 4 :(得分:2)

我认为可以针对一组固定类型进行,但需要做很多工作。您需要为每种类型定义一个特化,但应该可以使用编译时断言来检查唯一性。我假设一个STATIC_ASSERT(const_expr),就像Boost.StaticAssert中的一样,如果表达式为假,会导致编译失败。

假设我们有一组我们想要唯一ID的类型 - 本例中只有3个:

class TypeA;
class TypeB;
typedef int TypeC;

我们想要一种比较类型的方法:

template <typename T, typename U> struct SameType
{
    const bool value = false;
};

template <typename T> struct SameType<T,T>
{
    const bool value = true;
};

现在,我们定义了要枚举的所有类型的排序:

template <typename T> struct Ordering {};

template <> struct Ordering<void>
{
    typedef TypeC prev;
    typedef TypeA next;
};

template <> struct Ordering<TypeA>
{
    typedef void  prev;
    typedef TypeB next;
};

template <> struct Ordering<TypeB>
{
    typedef TypeA prev;
    typedef TypeC next;
};

template <> struct Ordering<TypeC>
{
    typedef TypeB prev;
    typedef void  next;
};

现在我们可以定义唯一ID:

template <typename T> struct TypeInt
{
    STATIC_ASSERT(SameType<Ordering<T>::prev::next, T>::value);
    static int value = TypeInt<T>::prev::value + 1;
};

template <> struct TypeInt<void>
{
    static int value = 0;
};

注意:我没有尝试编译任何此类内容。它可能需要在几个地方添加typename,它可能根本不起作用。

你不能希望将所有可能的类型映射到整数字段,因为它们有无限数量:具有任意间接级别的指针类型,任意大小和等级的数组类型,具有任意数量参数的函数类型等等。

答案 5 :(得分:2)

我不知道将编译时常量整数映射到类型的方法,但我可以给你下一个最好的东西。此示例演示了一种为类型生成唯一标识符的方法 - 虽然它不是整型常量表达式 - 通常会在编译时进行评估。如果您需要在类型和唯一的非类型模板参数之间进行映射,那么它也很有用。

struct Dummy
{
};

template<typename>
struct TypeDummy
{
    static const Dummy value;
};

template<typename T>
const Dummy TypeDummy<T>::value = Dummy();

typedef const Dummy* TypeId;

template<typename T, TypeId p = &TypeDummy<T>::value>
struct TypePtr
{
    static const TypeId value;
};

template<typename T, TypeId p>
const TypeId TypePtr<T, p>::value = p;

struct A{};

struct B{};

const TypeId typeA = TypePtr<A>::value;
const TypeId typeB = TypePtr<B>::value;

我将此开发为使用typeid(A) == typeid(B)排序类型的性能问题的解决方法,某些编译器在编译时无法对其进行评估。能够存储TypeId值以便在运行时进行比较也很有用:例如someType == TypePtr<A>::value

答案 6 :(得分:1)

这可能是做了一些“坏事”,并且可能会以某种微妙的方式违反标准......但是我还是想分享一下......也许其他人可以将它消毒成100%合法的东西?但它似乎适用于我的编译器。

逻辑是这个..为你感兴趣的每种类型构造一个静态成员函数并获取它的地址。然后将该地址转换为int。有点可疑的位是:1)函数ptr到int转换。 2)我不确定标准是否保证静态成员函数的地址都将正确合并以便在不同的编译单元中使用。

typedef void(*fnptr)(void);

union converter
{
  fnptr f;
  int i;
};

template<typename T>
struct TypeInt
{
  static void dummy() {}
  static int value() { converter c; c.f = dummy; return c.i; }
};

int main()
{
  std::cout<< TypeInt<int>::value() << std::endl;
  std::cout<< TypeInt<unsigned int>::value() << std::endl;
  std::cout<< TypeInt< TypeVoidP<int> >::value() << std::endl;
}

答案 7 :(得分:0)

如果没有自己分配数字或者只有一个文件知道所有类型,我认为这是不可能的。即使这样,你也会遇到模板类的麻烦。您是否必须为每个可能的类实例分配编号?

答案 8 :(得分:0)

type2int即使在C ++ 11中也不可能编译时间常量。也许一些有钱人应该答应对她的奖励?在那之前,我正在使用以下解决方案,这基本上与Matthew Herrmann的相同:

class type2intbase {
    template <typename T>
    friend struct type2int;

    static const int next() {
        static int id = 0; return id++;
    }
};

template <typename T>
struct type2int {
    static const int value() {
        static const int id = type2intbase::next(); return id;
    }
};

另请注意

template <typename T>
struct type2ptr {
    static const void* const value() {
        return typeid(T).name();
    }
};