Шаблонная метапрограмма, преобразовывающая тип в уникальное число

Причина это происходит, состоит в том, потому что затмение запускает Ваше приложение как фоновый процесс и не как процесс верхнего уровня с системной консолью.

11
задан daramarak 22 November 2011 в 12:34
поделиться

6 ответов

The closest I've come so far is being able to keep a list of types while tracking the distance back to the base (giving a unique value). Note the "position" here will be unique to your type if you track things correctly (see the main for the example)

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;

}

This works, but naturally I'd like to not have to specify a "prev" type somehow. Preferably figuring out a way to track this automatically. Maybe I'll play with it some more to see if it's possible. Definitely an interesting/fun puzzle.

3
ответ дан 3 December 2019 в 03:04
поделиться

В принципе, это возможно, хотя решение, вероятно, не то, что вы ищете.

Короче говоря, вам необходимо предоставить явное сопоставление типов с целочисленными значениями с одной записью для каждого возможного типа:

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 };
};
8
ответ дан 3 December 2019 в 03:04
поделиться

Я думаю, что это можно сделать для фиксированного набора типов, но это довольно большая работа. Вам нужно будет определить специализацию для каждого типа, но должна быть возможность использовать утверждения времени компиляции для проверки уникальности. Я предполагаю, что STATIC_ASSERT (const_expr) , как и в Boost.StaticAssert, вызывает сбой компиляции, если выражение ложно.

Предположим, у нас есть набор типов, которым нам нужны уникальные идентификаторы для - всего 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;
};

Теперь мы можем определить уникальный идентификатор:

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 в нескольких местах, и это может вообще не работать.

Вы можете '

2
ответ дан 3 December 2019 в 03:04
поделиться

Я не думаю, что это возможно без присвоения номеров самостоятельно или без единого файла, знающего обо всех типах. И даже тогда вы столкнетесь с проблемами с шаблонными классами. Нужно ли назначать номер для каждого возможного экземпляра класса?

0
ответ дан 3 December 2019 в 03:04
поделиться

Это может делать "плохие вещи" и, вероятно, каким-то тонким образом нарушает стандарт ... но все равно поделюсь ... может быть, что-то еще кто-то может продезинфицировать это до чего-то 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;
}
1
ответ дан 3 December 2019 в 03:04
поделиться

Это делает то, что вы хотите. Значения присваиваются по необходимости. Используется способ назначения статики в функциях.

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;
}

Это не шаблонное метапрограммирование на стероидах, но я считаю это хорошей вещью (поверьте мне!)

.
7
ответ дан 3 December 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: