Рекурсивное явное создание экземпляра шаблона для экспорта символов для библиотеки - PullRequest
8 голосов
/ 13 сентября 2011

В моем предыдущем вопросе я спросил возможно ли рекурсивное явное создание шаблона .Я видел, что это действительно возможно;однако эта реализация оказывается эффективной только локально, символы рекурсивно созданного шаблона не экспортируются в объектный файл и, следовательно, не отображаются в (общей) библиотеке.Поэтому я задаю вопрос здесь более точно, как в моем предыдущем посте:

Учитывая шаблон, такой как

template<int dim> class Point { ... };

, этот шаблон может быть явно создан, например,

template class Point<0>;
template class Point<1>;
template class Point<2>;
template class Point<3>;

, который экспортируетсимволы Point<0>, ..., Point<3> в объектный файл текущего блока перевода.Вместо того, чтобы создавать экземпляры каждого шаблона по отдельности, как описано выше, я хотел бы создать их рекурсивно с помощью всего одного вызова.

Любые решения, которые достигают этого, хороши, будь то в стиле шаблонного метапрограммирования, с помощью вспомогательного класса, такого как

template class RecursiveInstantiate<Point, 3>;

или с помощью препроцессора.Здесь я заглянул в библиотеку препроцессора boost, которая, кажется, имеет некоторые циклические конструкции.Однако я никогда не использовал библиотеку препроцессора boost (любой совет приветствуется), но на первый взгляд я скептически отношусь к тому, можно ли использовать циклы вместе с явным созданием шаблона.

Любой совет, а также объяснение, почему ононевозможно, то, чего я хочу достичь, приветствуется.


На самом деле я заинтересован в обобщении этого для классов с несколькими параметрами шаблона, такими как Node<int i1,int i2,int i3>, для всех комбинаций i1, i2, i3 в {0,1,2,3}.Но я надеюсь, что смогу решить эту вторую часть самостоятельно.Как обычно, я хочу использовать явные экземпляры для ускорения времени компиляции, определяя шаблоны только в одной единице перевода, поэтому мне нужны методы для экспорта шаблона в объектный файл.

Я надеюсь нарешение, не зависящее от компилятора, но если это невозможно, оно мне нужно для Linux с g ++ / clang.


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

Ответы [ 3 ]

6 голосов
/ 13 сентября 2011

Похоже на работу для Boost.Preprocessor :

#include <boost/preprocessor/repetition/repeat.hpp>

#define INSTANTIATE(_, n, type) template class type<n>;

BOOST_PP_REPEAT(3, INSTANTIATE, Point)

Конечно, вы можете встроить это в другой макрос, чтобы он выглядел лучше:

#define INSTANTIATE(_, n, type) template class type<n>;
#define INSTANTIATE_N(n, type) BOOST_PP_REPEAT(n, INSTANTIATE, type)

INSTANTIATE_N(3, Point)
4 голосов
/ 14 сентября 2011

Обзор решений

Посредством ускоренного препроцессора (BOOST_PP_REPEAT)

См. Решение, предложенное Люком Турайем

#define INSTANTIATE(_, n, type) template class type<n>;
#define INSTANTIATE_N(n, type) BOOST_PP_REPEAT(n, INSTANTIATE, type)
INSTANTIATE_N(4, Point)

Через шаблонное метапрограммирование

Смотрите решение и очень хорошее объяснение по phresnel ниже.Это звучит как предпочтительный подход.К сожалению, явная реализация может использоваться только на глобальном уровне (ограничение языка) и, следовательно, не может использоваться рекурсивно.Если создание экземпляра выполняется неявно (см. Мой предыдущий вопрос), то определяются только те символы (и, следовательно, экспортируемые в объектный файл), которые фактически используются, т. Е. Вам необходимо определить каждый символ класса один раз (с помощью фиктивного элемента).

Это не очень хороший подход, но он позволяет избежать, возможно, неприятных проблем (и проблем с переносимостью) решений препроцессора.

Через препроцессор Boost для нескольких параметров.

Наконец-то ятакже углубился в библиотеку предварительной обработки Boost и попытался расширить результат для создания экземпляров вида

  template class Node< int , 0 , 0 >;
  template class Node< int , 1 , 0 >;
  template class Node< int , 1 , 1 >;
  template class Node< int , 2 , 0 >;
  template class Node< int , 2 , 1 >;
  template class Node< int , 2 , 2 >;
  template class Node< float , 0 , 0 >;
  template class Node< float , 1 , 0 >;
  template class Node< float , 1 , 1 >;
  template class Node< float , 2 , 0 >;
  template class Node< float , 2 , 1 >;
  template class Node< float , 2 , 2 >;

Таким образом, шаблон Node<Scalar, pdim, ldim> с Scalar и арифметическим типом pdim и целыми числами и ldim <= pdim другое целое число.

Мне удалось расширить подход только с использованием BOOST_PP_REPEAT для шаблонов с двумя аргументами, поскольку BOOST_PP_REPEAT в настоящее время может иметь только вложенную глубину 3 уровня.Два уровня для параметров шаблона и один уровень для BOOST_PP_ENUM - это максимум, которого я мог достичь с помощью этой техники.Более гибко использовать технику итерации файла , которая поддерживает до 5 уровней.

Этот код, который мне удалось сгенерировать с помощью кода

#define INTTOTYPE0 (int, (float, (double, _)))
#define NUM_TEMPLATE_ARGS 3
#define MAX_TEMPLATE_PARAM0 2
#define MAX_TEMPLATE_PARAM1(i0) 2
#define MAX_TEMPLATE_PARAM2(i0, i1) i1
#define CLASSNAME Node 

#include "util/templateRecInstant.h"

из четырехэкземпляры для класса Point в вопросе могут быть сгенерированы с помощью

#define NUM_TEMPLATE_ARGS 1
#define MAX_TEMPLATE_PARAM0 3
#define CLASSNAME Point 

#include "util/templateRecInstant.h"

Оба метода выполняются с помощью файла "util / templateRecInstant.h" с содержанием

#if !BOOST_PP_IS_ITERATING

#define MY_FILE "util/templateRecInstant.h"

#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/list/at.hpp>

#ifndef NUM_TEMPLATE_ARGS
#error need to define NUM_TEMPLATE_ARGS
#endif

#ifndef MAX_TEMPLATE_PARAM0
#error need to specify MAX_TEMPLATE_PARAM0,  MAX_TEMPLATE_PARAM1, ..., up tp NUM_TEMPLATE_ARGS
#endif

#ifndef DEFAULT_INTTOTYPE
#define DEFAULT_INTTOTYPE (0, (1, (2, (3, (4, (5, (6, (7, (8, (9, _))))))))))
#endif

#ifndef INTTOTYPE0
#define INTTOTYPE0 DEFAULT_INTTOTYPE
#endif
#ifndef INTTOTYPE1
#define INTTOTYPE1 DEFAULT_INTTOTYPE
#endif
#ifndef INTTOTYPE2
#define INTTOTYPE2 DEFAULT_INTTOTYPE
#endif
#ifndef INTTOTYPE3
#define INTTOTYPE3 DEFAULT_INTTOTYPE
#endif

#if NUM_TEMPLATE_ARGS > 0
   #define BOOST_PP_ITERATION_PARAMS_1 (3, (0, \
         MAX_TEMPLATE_PARAM0, MY_FILE ))
   #include BOOST_PP_ITERATE()
#endif

#if NUM_TEMPLATE_ARGS == 0
  template class CLASSNAME< \
  >;
#endif

#undef MY_FILE
#undef NUM_TEMPLATE_ARGS 
#undef CLASSNAME 
#undef MAX_TEMPLATE_PARAM0
#undef MAX_TEMPLATE_PARAM1
#undef MAX_TEMPLATE_PARAM2
#undef MAX_TEMPLATE_PARAM3
#undef INTTOTYPE0
#undef INTTOTYPE1
#undef INTTOTYPE2
#undef INTTOTYPE3


#elif BOOST_PP_ITERATION_DEPTH() == 1

#if NUM_TEMPLATE_ARGS > 1
   #define BOOST_PP_ITERATION_PARAMS_2 (3, (0, \
         MAX_TEMPLATE_PARAM1(BOOST_PP_FRAME_ITERATION(1)), \
         MY_FILE))
   #include BOOST_PP_ITERATE()
#endif

#if NUM_TEMPLATE_ARGS == 1
  template class CLASSNAME< \
  BOOST_PP_LIST_AT( INTTOTYPE0, BOOST_PP_FRAME_ITERATION(1)) \
  >;
#endif

#elif BOOST_PP_ITERATION_DEPTH() == 2

#if NUM_TEMPLATE_ARGS > 2
   #define BOOST_PP_ITERATION_PARAMS_3 (3, (0, \
         MAX_TEMPLATE_PARAM2(BOOST_PP_FRAME_ITERATION(1) \
                           , BOOST_PP_FRAME_ITERATION(2) \
           ), \
         MY_FILE))
   #include BOOST_PP_ITERATE()
#endif

#if NUM_TEMPLATE_ARGS == 2
  template class CLASSNAME< \
    BOOST_PP_LIST_AT( INTTOTYPE0, BOOST_PP_FRAME_ITERATION(1)) \
  , BOOST_PP_LIST_AT( INTTOTYPE1, BOOST_PP_FRAME_ITERATION(2)) \
  >;
#endif

#elif BOOST_PP_ITERATION_DEPTH() == 3

#if NUM_TEMPLATE_ARGS > 3
   #define BOOST_PP_ITERATION_PARAMS_4 (3, (0, \
         MAX_TEMPLATE_PARAM3(BOOST_PP_FRAME_ITERATION(1) \
                           , BOOST_PP_FRAME_ITERATION(2) \
                           , BOOST_PP_FRAME_ITERATION(3) \
           ), \
         MY_FILE))
   #include BOOST_PP_ITERATE()
#endif

#if NUM_TEMPLATE_ARGS == 3
  template class CLASSNAME< \
    BOOST_PP_LIST_AT( INTTOTYPE0, BOOST_PP_FRAME_ITERATION(1)) \
  , BOOST_PP_LIST_AT( INTTOTYPE1, BOOST_PP_FRAME_ITERATION(2)) \
  , BOOST_PP_LIST_AT( INTTOTYPE2, BOOST_PP_FRAME_ITERATION(3)) \
  >;
#endif

#elif BOOST_PP_ITERATION_DEPTH() == 4

#if NUM_TEMPLATE_ARGS == 4
  template class CLASSNAME< \
    BOOST_PP_LIST_AT( INTTOTYPE0, BOOST_PP_FRAME_ITERATION(1)) \
  , BOOST_PP_LIST_AT( INTTOTYPE1, BOOST_PP_FRAME_ITERATION(2)) \
  , BOOST_PP_LIST_AT( INTTOTYPE2, BOOST_PP_FRAME_ITERATION(3)) \
  , BOOST_PP_LIST_AT( INTTOTYPE3, BOOST_PP_FRAME_ITERATION(4)) \
  >;
#endif

#if NUM_TEMPLATE_ARGS > 4
#error "NUM_TEMPLATE_ARGS > 4 is not supported (limitation by boost)"
#endif

#endif

Если вы оступилисьпо этой проблеме, не стесняйтесь использовать приведенный выше код.Для генерации этого кода я использовал 1 в качестве ссылки.

2 голосов
/ 13 сентября 2011

Проблема, с которой вы сталкиваетесь: Действительно, RecursiveInstantiate создан полностью.Но «вложенные» типы создаются только в той степени, в которой вы , используя их внутри RecursiveInstantiate.То есть, если вы не вызываете какую-то функцию Point::xxx, то xxx не будет создан, что является обычным поведением.Вам понадобится синтаксис для явного создания экземпляров внутри классов или функций.

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

Например,

template <int D> struct Foo {
    static void print();
};

#include <iostream>

int main () {
    Foo<0>::print();
    Foo<1>::print();
    Foo<2>::print();
}

затем

#include <iostream>

// Our Foo we'd like to instantiate explicitly and recursively.
template <int D> struct Foo {
    static void print() { std::cout << D << std::endl; }
};


template <int D>
static void instantiate () {
    // refer to everything that should be exported
    Foo<D>::print();
}

template <int D>
struct loop { 
    static void print_inst () { 
        instantiate<D>(); 
        loop<D-1>::print_inst();
    }
};

template <>
struct loop<0> { 
    static void print_inst () { 
        instantiate<0>();
    }
};

template struct loop<2>;

в функции instantiate вы бы ссылались на все, что должно быть экспортировано.

Вы должны взвесить лучший компромисс, возможно, мой подход самый короткий, возможно, ручной набор текста (template class Foo<0>; template class Foo<1> ...) короче, потому что у вас много функций-членов, возможно, вы считаете способ BOOST достаточно переносимым, даже еслистандарт не разрешает его.

Было бы интересно, если бы существовал синтаксис для явной реализации в неглобальных контекстах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...