【问题标题】:Recursive explicit template instantiation to export symbols for a library递归显式模板实例化以导出库的符号
【发布时间】:2011-11-15 23:18:44
【问题描述】:

在我之前的问题中,我问过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&lt;0&gt;, ..., Point&lt;3&gt; 的符号导出到当前翻译单元的目标文件中。我不想像上面那样单独实例化每个模板,我想只用一个调用递归地实例化它们。

任何实现这一点的解决方案都很好,无论是模板元编程的风格,通过一个帮助类,比如

template class RecursiveInstantiate<Point, 3>;

或通过预处理器。在这里,我查看了 boost 预处理器库,它似乎有一些循环结构。但是,我从未使用过 boost 预处理器库(任何建议都值得赞赏),但乍一看,我怀疑循环是否可以与显式模板实例化一起使用。

感谢任何建议,以及解释为什么我想要实现的目标是不可能的。


事实上,对于具有多个模板参数的类(例如Node&lt;int i1,int i2,int i3&gt;),对于 {0,1,2,3} 中的 i1,i2,i3 的所有组合,我很感兴趣。但我希望能够自己完成第二部分。像往常一样,我想通过仅在一个翻译单元中定义模板来使用显式实例化来加快编译时间,因此我需要将模板方法导出到目标文件中。

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


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

【问题讨论】:

  • 我从来没有遇到过使用 g++/mingw 显式实例化模板的问题。可能您已将它们放入未命名的命名空间中? (另见stackoverflow.com/questions/7182359/…,“不同翻译单元中的模板定义”部分)
  • @phresnel:你是指我下面的@Trilinos 吗?我不确定我是否理解你的问题。显式实例化 template class Point&lt;0&gt;; 工作得非常好,只是 recursive 显式实例化,通过模板元编程(参见上面链接的帖子),它不会在目标文件中导出符号。
  • 对不同的解决方案和您保留的解决方案进行调查是个好主意,但您可能应该将其放在自己的答案中,而不是“污染”原始问题(另外,您可能由于您提供了有趣的信息,因此获得了一些额外的声誉)。
  • @Luc Touraill:你说得对,我相应地改变了它。

标签: c++ templates boost-preprocessor


【解决方案1】:

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

【讨论】:

  • 这看起来像我需要的。 BOOST_PP_REPEAT(4, INSTANTIATION, Point) 完全等价于上面四行。我现在将尝试将其概括为多个整数参数Node&lt;int i1,int i2,int i3&gt;,并在找到解决方案后报告。
【解决方案2】:

解决方案调查

通过 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 的解决方案和非常好的解释。这听起来像是更可取的方法。不幸的是,显式实例化只能在全局级别使用(语言限制),因此不能递归使用。如果实例化是隐式完成的(请参阅我的 previos 问题),则仅定义实际使用的那些符号(并因此导出到目标文件),即您需要定义类的每个符号一次(使用虚拟对象)。

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

通过 Boost Preprocessor 处理多个参数。

最后,我还深入研究了 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&lt;Scalar, pdim, ldim&gt; 带有Scalar 和算术类型pdim 和整数和ldim &lt;= pdim 另一个整数。

我只能将 BOOST_PP_REPEAT 用于具有两个参数的模板扩展该方法,因为 BOOST_PP_REPEAT 目前只能嵌套 3 层深。模板参数的两个级别和BOOST_PP_ENUM 的一个级别是我使用这种技术可以达到的最大值。使用最多支持 5 个级别的file iteration 技术更加灵活。

这个代码我可以用代码生成

#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 作为参考。

【讨论】:

    【解决方案3】:

    您面临的问题是:确实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&lt;0&gt;; template class Foo&lt;1&gt; ...) 更短,因为您有许多成员函数,可能您认为 BOOST 方式足够便携,即使标准确实如此不批准。

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

    【讨论】:

    • 这是对正在发生的事情的一个很好的解释。我将尝试使 Boost 预处理器解决方案正常工作,因为我有不止一个类并且不想列出所有成员。那么我会接受你和卢克的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 2014-11-01
    • 2021-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多