2011-04-19 6 views
17

considere el siguiente archivo de cabecera:¿Cómo crear una instancia explícita de una plantilla para todos los miembros del vector MPL en C++?

// Foo.h 
class Foo { 
    public: 
     template <typename T> 
     void read(T& value); 
}; 

Quiero crear una instancia explícitamente la plantilla de función Foo::read miembro en un archivo de origen para todos los tipos incluidos en un boost::mpl::vector:

// Foo.cc 
#include <boost/mpl/vector.hpp> 
#include <boost/mpl/begin_end.hpp> 
#include "Foo.h" 

template <typename T> 
void Foo::read(T& value) { /* do something */ } 

typedef boost::mpl::vector<int, long, float> types; 

// template Foo::read<int >(int&); 
// template Foo::read<long >(long&); 
// template Foo::read<float>(float&); 

// instantiate automatically ??? 

¿Es posible? Gracias de antemano, Daniel.

EDITAR

he encontrado alguna solución - parece que la asignación de un puntero a Foo::read<T> en el constructor de una estructura, de los cuales variable se declara a continuación, la causa de instancias:

// intermezzo 
template <typename T> struct Bar { 
    Bar<T>() { 
     void (Foo::*funPtr)(T&) = &Foo::read<T>; 
    } 
}; 

static Bar<int > bar1; 
static Bar<long > bar2; 
static Bar<float> bar3; 

Entonces el proceso puede automatizarse de la siguiente manera:

// Foo.cc continued 
template <typename B, typename E> 
struct my_for_each { 
    my_for_each<B, E>() { 
     typedef typename B::type T;  // vector member 
     typedef void (Foo::*FunPtr)(T&); // pointer to Foo member function 
     FunPtr funPtr = &Foo::read<T>; // cause instantiation? 
    } 

    my_for_each<typename boost::mpl::next<B>::type, E> next; 
}; 

template<typename E> 
struct my_for_each<E, E> {}; 

static my_for_each< boost::mpl::begin<types>::type, 
        boost::mpl::end<types>::type > first; 

Pero no sé si Thi ¿La solución es portátil y cumple con los estándares? (Funciona con compiladores de Intel y GNU).

+3

nitpick: la pregunta podría ser un poco más claro si su clase 'foo' realidad cabeza un miembro de' 'read' en Foo.h' – Mat

+0

@nitpick: editado, por mi culpa, gracias –

+1

corto de usar preprocesador soluciones basadas, no creo que sea posible. –

Respuesta

0

No estoy seguro de si esta es la solución a su problema, pero tal vez se puede hacer con una plantilla de especialización.

nueva cabecera:

// Foo.h 

template < typename T > 
struct RealRead; 

class Foo { 
    public: 
     template <typename T> 
     void read(T& value); 
}; 

template <typename T> 
void Foo::read(T& value) 
{ 
    RealRead<T>::read(value); 
} 

Nueva fuente:

template < > 
struct RealRead<int> 
{ 
    static void read(int & v) 
    { 
    // do read 
    } 
}; 
template < > 
struct RealRead<float> 
{ 
    static void read(float & v) 
    { 
    // do read 
    } 
}; 

//etc 

// explicitly instantiate templates 
template struct RealRead<int>; 
template struct RealRead<float>; 
1

Usted puede crear una instancia explícita Foo como un parámetro de plantilla dada T con template class Foo<T>;

En cuanto a la creación de instancias de proceso por lotes, no lo creo creo que es posible. Tal vez con plantillas variadic es posible crear una clase de Instantiate para que algo así como Instantiate<Foo, int, short, long, float, etc> instanciara las plantillas apropiadas, pero aparte de eso, debe recurrir a la creación de instancias manual.

0

instanciación explícita tiene una gramática especial y un significado especial para el compilador, por lo que no se puede hacer con metaprogramación.

su solución causa una creación de instancias, pero no una instanciación explícita.

0

No creo que sea necesario, ni posible.

Puede usar directamente (llamar) la función Foo: leer (bar), para barra variable de cualquier tipo, siempre que el tipo esté bien definido en la implementación de su función de plantilla. El compilador transformará automáticamente su argumento en tipo "T".

Por ejemplo:

template <class T> 
Foo::read(T & var) 
{ 
    std::cin >> var; 
} 

T está bien definido cuando T es un tipo de streaming apoyado por cin.

El ejemplo será independiente, si se elimina "Foo ::". Quiero decir, para "Foo ::", debería haber definido en algún lugar una clase Foo, o un espacio de nombres Foo, para que funcione.

Sin embargo, tenga en cuenta que la plantilla siempre debe ir dentro de un archivo .h, no un archivo .cpp (simplemente buscar en la web con la palabra clave "C++ plantilla no se puede implementar en cpp"

+0

La definición de la plantilla en un archivo .cpp, luego de instanciarlo (más o menos) explícitamente es una técnica habitual cuando se conocen los argumentos con los que se debe instanciar la plantilla. Hay mucho que ganar en el lado de la dependencia del encabezado. – Quentin

0

Si se va a utilizar su clase solo en un módulo individual (es decir, no la exportará) puede usar boost/mpl/for_each. La función de plantilla definida de esta manera (usando mpl/for_each) no se exportará (incluso si declara __declspec (export) antes del nombre de la clase o firma de función):

// Foo.cpp 
#include <boost/mpl/vector.hpp> 
#include <boost/mpl/for_each.hpp> 

template<class T> 
void read(T& value) 
{ 
... 
} 

using types = boost::mpl::vector<long, int>; 

//template instantiation 
struct call_read { 
    template <class T> 
    void operator()(T) 
    { 
    T t; //You should make sure that T can be created this way 
    ((Foo*)nullptr)->read<T>(t); //this line tells to compiler with templates it should instantiate 
    } 
}; 

void instantiate() 
{ 
    boost::mpl::for_each<types>(call_read()); 
} 

Si necesita métodos de exportación/importación a estructurar y de la plantilla no es la solución con realce/preprocesador

// Foo.h 
#ifdef <preprocessor definition specific to DLL> 
# define API __declspec(dllexport) 
#else 
# define API __declspec(dllimport) 
#endif 

class API Foo { 
public: 
    template<class T> void read(T& value); 
}; 

// Foo.cpp 
#include <boost/preprocessor/seq/for_each.hpp> 
#include <boost/preprocessor/seq/enum.hpp> 
#include <boost/mpl/vector.hpp> 

template<class T> 
void read(T& value) 
{ 
... 
} 

//using this macro you can define both boost::mpl structure AND instantiate explicitly your template function 
#define VARIANT_LIST (std::wstring)(long)(int) 
using types = boost::mpl::vector<BOOST_PP_SEQ_ENUM(VARIANT_LIST)>; 

//Here we should use our API macro 
#define EXPLICIT_INSTANTIATION(r, d, __type__) \ 
    template API void Foo::read<__type__>(__type__&); 
BOOST_PP_SEQ_FOR_EACH(EXPLICIT_INSTANTIATION, _, VARIANT_LIST) 

Si no necesita esta funcionalidad adicional la primera solución es mucho más limpio que supongo

0

que he tenido el mismo requisito no hace mucho tiempo, y tuvo buenos resultados con una plantilla de instancias función simple :

template <class... T> 
void forceInstantiation(typedef boost::mpl::vector<T...>*) { 

    using ex = int[]; 
    (void)ex{(void(&Foo::read<T>), 0)..., 0}; 

    // C++17 
    // (void)((void(&Foo::read<T>), ...)); 
} 

template void forceInstantiation(types*); 
Cuestiones relacionadas