クラス定義を宣言から自動的に分離しますか?
-
19-08-2019 - |
質問
次のように、ヘッダーファイルのテンプレートクラスおよび関数でほぼ完全に構成されるライブラリを使用しています:
// foo.h
template<class T>
class Foo {
Foo(){}
void computeXYZ() { /* heavy code */ }
};
template<class T>
void processFoo(const Foo<T>& foo) { /* more heavy code */ }
これらのヘッダーファイルの1つをインクルードするたびに(コンパイル時間が耐えられないため、これは悪いことです。実際には、各コンパイルユニットにそれらの多くをインクルードします。)
テンプレートパラメータとして1つまたは2つのタイプのみを使用するため、ライブラリヘッダーファイルごとに、宣言のみを含むファイルを、次のような重いコードなしで作成する予定です。
// NEW: fwd-foo.h
template<class T>
class Foo {
Foo();
void computeXYZ();
};
template<class T>
void processFoo(const Foo<T>& foo);
次に、必要なすべてのインスタンス化を作成する1つのファイル。そのファイルは、一度だけ個別にコンパイルすることができます:
// NEW: foo.cpp
#include "foo.h"
template class Foo<int>;
template class Foo<double>;
template void processFoo(const Foo<int>& foo);
template void processFoo(const Foo<double>& foo);
コードにfwd-foo.h
を含めるだけで、コンパイル時間が短くなります。最後にfoo.o
に対してリンクします。
もちろん、欠点は、これらの新しいfoo.cpp
およびfoo.h
ファイルを自分で作成する必要があることです。そしてもちろん、メンテナンスの問題です。新しいライブラリバージョンがリリースされたら、それらをその新しいバージョンに適合させる必要があります。他に欠点はありますか?
そして私の主な質問は:
これらの新しいファイル、特に元のextern
から自動的に自動的に作成できる可能性はありますか?多くのライブラリヘッダーファイル(おそらく20程度)でこれを行う必要があり、特に新しいライブラリバージョンがリリースされ、新しいバージョンでこれを再度行う必要がある場合は、自動ソリューションが最適です。このタスクに使用できるツールはありますか?
編集:
追加の質問:この場合、新しくサポートされる<=>キーワードはどのように役立ちますか?
解決
lzz を使用します。これにより、単一のファイルが個別のヘッダーと翻訳単位に分割されます。デフォルトでは、通常はテンプレート定義もヘッダーに配置されますが、これを行わないように指定できます。
どのように使用するかを示すために、以下を考慮してください:
// t.cc
#include "b.h"
#include "c.h"
template <typename T>
class A {
void foo () {
C c;
c.foo ();
b.foo ();
}
B b;
}
上記のファイルを取得して、「t.lzz」ファイルにコピーします。必要に応じて、 #include ディレクティブを個別の$ hdrブロックと$ srcブロックに配置します。
// t.lzz
$hdr
#include "b.h"
$end
$src
#include "c.h"
$end
template <typename T>
class A {
void foo () {
C c;
c.foo ();
b.foo ();
}
B b;
}
最後に、テンプレート定義をソースファイルに配置することを指定して、ファイルに対してlzzを実行します。ソースファイルで $ pragma を使用してこれを行うか、コマンドラインオプション<!> quot; -ts <!> quot;:
を使用できます。これにより、次のファイルが生成されます。
// t.h
//
#ifndef LZZ_t_h
#define LZZ_t_h
#include "b.h"
#undef LZZ_INLINE
#ifdef LZZ_ENABLE_INLINE
#define LZZ_INLINE inline
#else
#define LZZ_INLINE
#endif
template <typename T>
class A
{
void foo ();
B b;
};
#undef LZZ_INLINE
#endif
そして:
// t.cpp
//
#include "t.h"
#include "c.h"
#define LZZ_INLINE inline
template <typename T>
void A <T>::foo ()
{
C c;
c.foo ();
b.foo ();
}
#undef LZZ_INLINE
これらをいくつかのgrep / sedコマンドで実行して、LZZヘルパーマクロを削除できます。
他のヒント
プリコンパイル済みヘッダーを使用してみてください。 GCCとMSVCがこの機能をサポートしていることを知っています。ただし、使用法はベンダー固有です。
私はかなり長い間、同じ問題に取り組んでいます。提案しているソリューションでは、テンプレートクラスを2回定義しています。同じものを(同じ順序で)定義すれば問題ありませんが、遅かれ早かれ問題が発生することになります。
私が思いついたのは、問題を逆に考えることです。実装に特化していない限り、正常に機能します。
2つのマクロを使用し、実装ファイルのテンプレート引数を更新する必要がありません(ただし、デフォルトのテンプレート引数をクラスに追加する場合は注意してください)。
// foo.h
#define FOO_TEMPLATE template<typename T>
#define FOO_CLASS Foo<T>
FOO_TEMPLATE
class Foo {
Foo();
void computeXYZ();
};
// foo_impl.h
#include "foo.h"
FOO_TEMPLATE
FOO_CLASS::Foo(){}
FOO_TEMPLATE
void FOO_CLASS::computeXYZ() { /* heavy code */ }
これを行うことにより、基本的に非テンプレートクラスで行うのと同じように動作します(もちろん、テンプレート関数でも同じことを行うことができます)。
編集:c ++ 0xのexternキーワードについて
c ++ 0xのexternキーワードが役立つと思いますが、魔法のようにすべてを解決することはできません!
この記事、
から外部テンプレート
インスタンス化するすべてのモジュール テンプレートは本質的にのコピーを作成します オブジェクトコード内で。その後、それはアップです リンカーにすべての 最後の冗長オブジェクトコード ステージ、したがってクリティカルを遅くする 構成する編集-コンパイル-リンクのサイクル プログラマーの日(または時々 空想)。これを短絡するには オブジェクトコードガベージコレクション、 コンパイラベンダーの数は externキーワードが既に実装されています テンプレートの前に配置できます。 これは標準化の事例です 既存の業界慣行の体系化 (しゃれを意図)。実際には、これは に通知を送信することにより実装 コンパイラは基本的に<!> quot;インスタンス化しない ここに<!> quot;:
extern template class std::vector;
C ++ 0xは、externテンプレートのコンパイル時の問題を修正します。しかし、あなたが尋ねたことを自動的に行う方法はわかりません。