tipos test razón programacion pnl neurolinguistica metaprogramas metaprograma metamodelos informatica igualador estilos elección diferenciador c++ templates metaprogramming

c++ - razón - test de metaprogramas



Tipo de conversión de metaprograma de plantilla a número único (9)

Creo que es posible hacerlo para un conjunto fijo de tipos, pero bastante trabajo. Tendrá que definir una especialización para cada tipo, pero debería ser posible usar asertos en tiempo de compilación para verificar la exclusividad. STATIC_ASSERT(const_expr) un STATIC_ASSERT(const_expr) , como el de Boost.StaticAssert, que causa un error de compilación si la expresión es falsa.

Supongamos que tenemos un conjunto de tipos para los que queremos ID únicos, solo 3 para este ejemplo:

class TypeA; class TypeB; typedef int TypeC;

Queremos una forma de comparar tipos:

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

Ahora, definimos un orden de todos los tipos que queremos enumerar:

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

Ahora podemos definir la ID única:

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

NOTA: No he intentado compilar nada de esto. Es posible que necesite agregar el nombre de tipo en algunos lugares, y puede que no funcione en absoluto.

No puede esperar asociar todos los tipos posibles a un campo entero, porque hay un número ilimitado de ellos: tipos de puntero con niveles arbitrarios de direccionamiento indirecto, tipos de matriz de tamaño y rango arbitrarios, tipos de funciones con números arbitrarios de argumentos, y así en.

Empecé a jugar con metaprogramación y estoy trabajando en diferentes tareas solo para explorar el dominio. Una de ellas era generar un entero único y asignarlo al tipo, como se muestra a continuación:

int myInt = TypeInt<AClass>::value;

Donde el valor debe ser una constante de tiempo de compilación, que a su vez puede usarse más en metaprogramas.

Quiero saber si esto es posible, y en ese caso cómo. Porque aunque he aprendido mucho sobre la exploración de este tema, todavía no he podido encontrar una respuesta.

(PD Una respuesta sí / no es mucho más gratificante que una solución c ++ que no usa metaprogramación, ya que este es el dominio que estoy explorando)


En principio, esto es posible, aunque la solución probablemente no sea lo que estás buscando.

En resumen, debe proporcionar una asignación explícita de los tipos a los valores enteros, con una entrada para cada tipo posible:

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;

Si no proporciona la implementación alternativa en la plantilla base, esto fallará para los tipos desconocidos si T , de lo contrario devolvería el valor de retorno.

Dependiendo de su contexto, puede haber otras posibilidades, también. Por ejemplo, podría definir esos números dentro de los tipos en sí mismos:

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

Si le da más contexto, puede haber otras soluciones, también.

Editar :

En realidad, no hay más contexto para eso. Estaba investigando si realmente era posible, pero sin asignar los números en sí.

Creo que la idea de Mike de ordenar es una buena manera de hacer esto (nuevamente, para un conjunto fijo de tipos ) sin tener que asignar números explícitamente: están implícitamente dados por el orden. Sin embargo, creo que esto sería más fácil usando una lista de tipos. El índice de cualquier tipo en la lista sería su número. Creo que algo como lo siguiente podría hacer:

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


Lo más cerca que he llegado hasta ahora es poder mantener una lista de tipos mientras rastreo la distancia de regreso a la base (dando un valor único). Tenga en cuenta que la "posición" aquí será única para su tipo si realiza un seguimiento de las cosas correctamente (consulte el principal para ver el ejemplo)

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

Esto funciona, pero, naturalmente, me gustaría no tener que especificar un tipo "anterior" de alguna manera. Preferiblemente descubriendo una forma de rastrear esto automáticamente. Quizás jugaré con eso un poco más para ver si es posible. Definitivamente un rompecabezas interesante / divertido.


No creo que sea posible sin asignar los números usted mismo o tener un solo archivo sobre todos los tipos. E incluso entonces tendrá problemas con las clases de plantilla. ¿Tiene que asignar el número para cada posible instanciación de la clase?


Esto hace lo que quieres. Los valores se asignan según la necesidad. Aprovecha la forma en que se asignan estáticas en las funciones.

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

No es metaprogramación de plantillas con esteroides, pero lo considero algo bueno (¡créanme!)


Esto puede estar haciendo algunas "cosas malas" y probablemente viole el estándar de algunas maneras sutiles ... pero pensé que lo compartiría de todos modos ... ¿tal vez alguien más puede desinfectarlo en algo 100% legal? Pero parece funcionar en mi compilador.

La lógica es esta ... construir una función miembro estática para cada tipo que le interese y tomar su dirección. Luego convierte esa dirección a un int. Los bits que son un poco sospechosos son: 1) la función ptr to int conversion. y 2) No estoy seguro de que el estándar garantice que las direcciones de las funciones miembro estáticas se fusionarán correctamente para los usos en diferentes unidades de compilación.

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


type2int como constante de tiempo de compilación es imposible incluso en C ++ 11. ¿Tal vez algún tipo rico debería prometer una recompensa por el awser? Hasta entonces estoy usando la siguiente solución, que es básicamente igual a la de 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; } };

Tenga en cuenta también

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


No conozco una forma de asignar un entero constante en tiempo de compilación a un tipo, pero puedo darte la siguiente mejor opción. Este ejemplo demuestra una forma de generar un identificador único para un tipo que, si bien no es una expresión de constante integral, generalmente se evaluará en tiempo de compilación. También es potencialmente útil si necesita una asignación entre un tipo y un argumento único de plantilla sin tipo.

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;

Desarrollé esto como una solución alternativa para los problemas de rendimiento con los tipos de ordenamiento que usan typeid(A) == typeid(B) , que un determinado compilador no puede evaluar en tiempo de compilación. También es útil poder almacenar valores de TypeId para la comparación en el tiempo de ejecución: por ejemplo, someType == TypePtr<A>::value


De forma similar al enfoque de Michael Anderson, esta implementación es totalmente compatible con los estándares y puede realizarse en tiempo de compilación. A partir de C ++ 17, parece que los valores constexpr podrán utilizarse como un parámetro de plantilla para otros propósitos de meta metaprogramación de plantillas. También unique_id_type se puede comparar con ==,! =,>, <, Etc. para fines de clasificación.

// 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(){}; };