8

以前の質問で私が尋ねたのは、再帰的な明示的なテンプレートのインスタンス化が可能であるということです。それが実際に可能であることがわかりました。ただし、このインスタンス化はローカルでのみ有効であることが判明し、再帰的にインスタンス化されたテンプレートのシンボルはオブジェクト ファイルにエクスポートされないため、(共有) ライブラリには表示されません。そこで、以前の投稿と同様に、ここでより正確に質問します。

次のようなテンプレートが与えられた場合

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>現在の翻訳単位のオブジェクトファイルにエクスポートします。上記のようにすべてのテンプレートを個別にインスタンス化する代わりに、1 回の呼び出しで再帰的にインスタンス化したいと考えています。

これを達成するソリューションは、テンプレートのメタプログラミングのスタイルであっても、次のようなヘルパー クラスを介しても問題ありません。

template class RecursiveInstantiate<Point, 3>;

またはプリプロセッサ経由。ここでは、いくつかのループ構造を持っているように見えるブースト プリプロセッサ ライブラリを調べました。ただし、boost プリプロセッサ ライブラリを使用したことはありません (アドバイスをいただければ幸いです)。

どんなアドバイスでも、私が達成したいことが不可能な理由の説明もいただければ幸いです。


Node<int i1,int i2,int i3>実際、{0,1,2,3} の i1、i2、i3 のすべての組み合わせのように、複数のテンプレート パラメーターを持つクラスに対してこれを一般化することに関心があります。しかし、私はこの第 2 部を自分で解決できることを望んでいます。いつものように、明示的なインスタンス化を使用して、1 つの翻訳単位でテンプレートを定義するだけでコンパイル時間を短縮したいので、テンプレート化するメソッドをオブジェクト ファイルにエクスポートする必要があります。

コンパイラに依存しないソリューションを望んでいますが、それが不可能な場合は、g++/clang を使用する Linux で必要です。


私が得た解決策の調査と、これから作成した最終的な解決策については、以下を参照してください。

4

3 に答える 3

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)
于 2011-09-13T09:17:21.637 に答える
4

ソリューションの調査

ブースト プリプロセッサ経由 (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による解決策と非常に良い説明を参照してください。これは好ましいアプローチのように思えます。残念ながら、明示的なインスタンス化はグローバル レベル (言語の制限) でしか使用できないため、再帰的に使用することはできません。インスタンス化が暗黙的に行われる場合 (前の質問を参照)、実際に使用されるシンボルのみが定義されます (したがって、オブジェクト ファイルにエクスポートされます)。つまり、クラスの各シンボルを一度 (ダミーで) 定義する必要があります。

これはあまり良い方法ではありませんが、プリプロセッサ ソリューションの厄介な問題 (および移植性の問題) を回避できます。

複数のパラメータの 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<Scalar, pdim, ldim>を含むテンプレート。Scalarpdimldim <= pdim

現在、3 レベルの深さしかネストできないBOOST_PP_REPEATため、2 つの引数を持つテンプレートを使用してアプローチを拡張することしかできませんでした。BOOST_PP_REPEATテンプレート パラメータの 2 つのレベルと 1 つのレベル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 クラスの 4 つのインスタンス化は、次の方法で生成できます。

#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を使用しました。

于 2011-09-14T07:27:51.940 に答える
2

あなたが直面している問題は次のとおりです: IndeedRecursiveInstantiateは完全にインスタンス化されています。ただし、「囲まれた」型は、内部で使用している程度にのみインスタンス化されます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>;

functioninstantiateでは、エクスポートする必要があるすべてのものを参照します。

最良の妥協点を検討する必要があります。おそらく私のアプローチが最短であり、多くのメンバー関数があるため、手動の typedown ( template class Foo<0>; template class Foo<1> ...) の方が短い可能性があります。標準で認可されていなくても、BOOST の方法が十分に移植可能であると考える可能性があります。

非グローバル コンテキストでの明示的なインスタンス化のための構文があれば興味深いでしょう。

于 2011-09-13T10:43:18.763 に答える