Как явно создать экземпляр шаблона для всех членов вектора MPL в C++?
Рассмотрим следующий заголовочный файл:
// Foo.h
class Foo {
public:
template <typename T>
void read(T& value);
};
Я хочу явно создать экземпляр Foo::read
шаблон функции-члена в исходном файле для всех типов, включенных в 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 ???
Является ли это возможным? Заранее спасибо, Даниэль.
РЕДАКТИРОВАТЬ
Я нашел какое-то решение - кажется, что присвоение указателя Foo::read<T>
в конструкторе структуры, переменная которой затем объявляется, вызывают создание экземпляров:
// 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;
Тогда процесс может быть автоматизирован следующим образом:
// 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;
Но я не знаю, является ли это решение портативным и совместимым со стандартами? (Работает с компиляторами Intel и GNU.)
6 ответов
Недавно у меня было такое же требование, и у меня были хорошие результаты с простой реализацией шаблона функции:
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*);
Я не думаю, что это необходимо, и это невозможно.
Вы можете напрямую использовать (вызывать) функцию Foo:read(bar) для переменной bar любого типа, если тип четко определен в реализации функции шаблона. Компилятор автоматически преобразует ваш аргумент в тип "T".
Например:
template <class T>
Foo::read(T & var)
{
std::cin >> var;
}
T хорошо определен, когда T является типом потоковой передачи, поддерживаемым cin.
Пример будет самодостаточным, если удалить "Foo::". Я имею в виду, что для "Foo::" вы должны были где-то определить класс Foo или пространство имен Foo, чтобы оно работало.
Тем не менее, обратите внимание, что шаблон должен всегда находиться внутри файла.h, а не файла.cpp (просто поищите в Интернете ключевое слово "шаблон C++ не может быть реализован в файле cpp")
Если вы намереваетесь использовать свой класс только в одном модуле (т.е. вы не будете его экспортировать), вы можете использовать boost/mpl/for_each. Функция шаблона, определенная таким образом (с использованием mpl/for_each), не будет экспортирована (даже если вы объявите __declspec(export) перед именем класса или сигнатурой функции):
// 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());
}
Если вам нужен экспорт / импорт вашей структуры и шаблонов, есть решение, использующее boost / препроцессор
// 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)
Если вам не нужна эта дополнительная функциональность, я думаю, первое решение намного чище
Явное создание имеет специальную грамматику и особое значение для компилятора, поэтому не может быть сделано с метапрограммированием.
Ваше решение вызывает создание экземпляра, но не явное создание.
Я не уверен, что это решение вашей проблемы, но, возможно, вы можете сделать со специализацией шаблона.
Новый заголовок:
// 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 );
}
Новый источник:
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 >;
Вы можете явно создать экземпляр Foo для заданного параметра шаблона T с помощью template class Foo<T>;
Что касается пакетной реализации, я не думаю, что это возможно. Может быть, с помощью шаблонов с переменными параметрами можно создать класс Instantiate, что-то вроде Instantiate<Foo, int, short, long, float, etc>
будет создавать соответствующие шаблоны, но кроме этого, вам придется прибегнуть к ручной реализации.