递归显式模板实例化以导出库的符号

时间:2011-09-13 08:35:57

标签: c++ templates boost-preprocessor

在上一个问题中,我问is recursive explicit template instantiation possible。我看到它确实可能;但是,此实例化仅在本地有效,递归实例化模板的符号不会导出到目标文件,因此不会出现在(共享)库中。所以我在这里更准确地提出问题,就像我在上一篇文章中所说的那样:

给出像

这样的模板
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预处理器库(任何建议都很受欢迎),但乍看之下,如果循环可以与显式模板实例化一起使用,我会持怀疑态度。

任何建议,也解释为什么我想达到的目标是不可能的。


事实上,我有兴趣为{0,1,2,3}中i1,i2,i3的所有组合使用多个模板参数(例如Node<int i1,int i2,int i3>)来推广这个。但我希望能够自己解决这第二部分。像往常一样,我想通过仅在一个转换单元中定义模板来使用显式实例化来加速编译时间,因此我需要将模板的方法导出到目标文件中。

我希望有一个独立于编译器的解决方案,但如果不可能,我需要用于带有g ++ / clang的Linux。


请参阅下文,了解我所获得的解决方案以及我从中获得的最终解决方案。

3 个答案:

答案 0 :(得分:6)

似乎是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)

答案 1 :(得分:4)

解决方案调查

通过Boost预处理器(BOOST_PP_REPEAT)

见Luc Touraille提出的解决方案

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

通过模板元编程

请参阅下面的phresnel解决方案和非常好的解释。这听起来像是更好的方法。不幸的是,显式实例化只能在全局级别使用(语言限制),因此不能递归使用。如果实例化是隐含的(参见我的普遍问题),那么只有那些符号被定义(并因此导出到目标文件)实际使用,即你需要定义类的每个符号一次(使用虚拟)。 / p>

这不是一个非常好的方法,但它避免了预处理器解决方案可能存在的令人讨厌的问题(和可移植性问题)。

通过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的一个级别是我使用此技术可以达到的最大值。使用file iteration技术可以更灵活地支持多达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 :(得分:2)

您面临的问题是:确实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方式足够便携,即使标准不会制裁它。

如果在非全局上下文中存在显式实例化的语法,那将会很有趣。