2011-01-25 12 views
9

Tengo la siguiente situación: supongamos que tengo un montón de tipos (funtores) que deseo registrar/compilar durante la compilación, preferiblemente en algo como boost :: mpl :: vector. ¿Conoces algún truco para hacerlo bien?Registro de tipo C++ en tiempo de compilación truco

Mi deseo es tener un archivo hpp que implemente el tipo de functor y el archivo de registro, donde una macro ingresa el tipo en la compilación.

Por ejemplo

// registered.hpp 
REGISTER("functor1.hpp") // implementation 
REGISTER("functor2.hpp") 
... 
boost::mpl::vector<...> types; // full registration vector 

Es de esperar que tenga sentido. Gracias

Respuesta

16

Hay una forma de registrar tipos uno por uno y luego recuperarlos en forma de mpl :: vector o similar. Aprendí este truco en las listas de correo de impulso (quizás de Dave Abrahams, aunque no puedo recordarlo con certeza).

Editar: Lo aprendí de la diapositiva 28 en https://github.com/boostcon/2011_presentations/raw/master/thu/Boost.Generic.pdf.

No usaré MPL en el código para hacerlo autónomo.

// The maximum number of types that can be registered with the same tag. 
enum { kMaxRegisteredTypes = 10 }; 

template <int N> 
struct Rank : Rank<N - 1> {}; 

template <> 
struct Rank<0> {}; 

// Poor man's MPL vector. 
template <class... Ts> 
struct TypeList { 
    static const int size = sizeof...(Ts); 
}; 

template <class List, class T> 
struct Append; 

template <class... Ts, class T> 
struct Append<TypeList<Ts...>, T> { 
    typedef TypeList<Ts..., T> type; 
}; 

template <class Tag> 
TypeList<> GetTypes(Tag*, Rank<0>) { return {}; } 

// Evaluates to TypeList of all types previously registered with 
// REGISTER_TYPE macro with the same tag. 
#define GET_REGISTERED_TYPES(Tag) \ 
    decltype(GetTypes(static_cast<Tag*>(nullptr), Rank<kMaxRegisteredTypes>())) 

// Appends Type to GET_REGISTERED_TYPES(Tag). 
#define REGISTER_TYPE(Tag, Type)        \ 
    inline Append<GET_REGISTERED_TYPES(Tag), Type>::type  \ 
    GetTypes(Tag*, Rank<GET_REGISTERED_TYPES(Tag)::size + 1>) { \ 
    return {};            \ 
    }               \ 
    static_assert(true, "") 

Ejemplo de uso:

struct IntegralTypes; 
struct FloatingPointTypes; 

// Initially both type lists are empty. 
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<>>::value, ""); 
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<>>::value, ""); 

// Add something to both lists. 
REGISTER_TYPE(IntegralTypes, int); 
REGISTER_TYPE(FloatingPointTypes, float); 
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<int>>::value, ""); 
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<float>>::value, ""); 

// Add more types. 
REGISTER_TYPE(IntegralTypes, long); 
REGISTER_TYPE(FloatingPointTypes, double); 
static_assert(std::is_same<GET_REGISTERED_TYPES(IntegralTypes), TypeList<int, long>>::value, ""); 
static_assert(std::is_same<GET_REGISTERED_TYPES(FloatingPointTypes), TypeList<float, double>>::value, ""); 
+0

¿Tiene un enlace al hilo de la lista de correo? – Xeo

+0

Desafortunadamente no puedo encontrar el hilo original del cual aprendí el truco. –

0

No utilizaría las macros. La técnica habitual es simplemente definir algún objeto cuya inicialización haga el registro. Obstáculo: necesita hacer referencia a algo, p. llamar a una función, en la unidad de compilación, con el fin de que se han vinculado en.

Saludos & HTH.,

+0

Idealmente me gustaría poner todos esos tipos en recipientes con una línea. Sé que puedo hacerlo de otra manera con 2-3 líneas, pero quiero saber si hay un truco. – Anycorn

-3

Nunca se va a resolver el MPL :: idea del vector. No puede alterar las "variables" de la plantilla. Recuerde que la metaprogramación de plantillas es un lenguaje funcional PURO. Sin ningún efecto secundario.

En cuanto al registro ... la macro funciona bien. Defina la macro para que declare e inicialice una pequeña variable global con el proceso de registro. Como alternativa se puede ir por el camino que hice aquí:

How to force inclusion of "unused" object definitions in a library

Nota de la revisión si está tratando de hacerlo de una biblioteca.

+0

mi idea era como: primer registro -> tipos1, agregar segundo para hacer tipos2, etc ... Finalmente tipodef tiposN a tipos. – Anycorn

+1

'mpl :: for_each' le da efectos secundarios en tiempo de ejecución. Es de suponer que puedes construir un 'mpl :: vector' de tipos con el que puedas hacer ALGO útil; algún tipo de cadena de política tal vez? De todos modos, tanto 'for_each' como la familia' fold' te permiten construir construcciones que pueden tener efectos de tiempo de ejecución. Mi favorito es tener una plantilla de contras del coche/cdr que pruebe contra un mpl :: contraint y llame a un miembro del coche o se expanda y llame al coche de cdr. – KitsuneYMG

+0

@ymg - Sí, pero no hay forma de construir una secuencia mpl desde ubicaciones múltiples e independientes. Lo único que puedes hacer es definir una secuencia en algún lugar y usarla. Entonces no habría ningún tema de "registro". No sería más eficiente que simplemente subclasificar la fábrica y codificar los tipos en su constructor. Menos tan en realidad. –

Cuestiones relacionadas