私は C++ スタティック ライブラリをコンパイルしています。すべてのクラスがテンプレート化されているため、クラスの定義と実装はすべてヘッダー ファイルにあります。その結果、(Visual Studio 2005 では) ライブラリに正しくコンパイルするために、他のすべてのヘッダー ファイルを含む .cpp ファイルを作成する必要があるようです。
どうしてこれなの?
私は C++ スタティック ライブラリをコンパイルしています。すべてのクラスがテンプレート化されているため、クラスの定義と実装はすべてヘッダー ファイルにあります。その結果、(Visual Studio 2005 では) ライブラリに正しくコンパイルするために、他のすべてのヘッダー ファイルを含む .cpp ファイルを作成する必要があるようです。
どうしてこれなの?
コンパイラはヘッダー ファイルをコンパイルしません。これは、ヘッダー ファイルがソース ファイルに含まれることが意図されているためです。コンパイルが行われる前に、プリプロセッサはインクルードされたヘッダー ファイルからすべてのコードを取得し、そのコードをインクルードされたソース ファイルのインクルードされた場所に配置します。コンパイラがヘッダーファイルもコンパイルする必要がある場合は、たとえば、多くのものに対して複数の定義が必要です。
例、これはプリプロセッサが見るものです:
[foo.h]
void foo();
--
[mysource.cpp]
#include "foo.h"
int main()
{
foo();
}
そして、これはコンパイラが見るものです:
[mysource.cpp]
void foo();
int main()
{
foo();
}
.cpp ファイルを作成しても、何も受け取りません。テンプレートをライブラリに配置するには、テンプレートをインスタンス化する必要があります。
具体的な型でテンプレートをインスタンス化する方法については、http://www.parashift.com/c%2B%2B-faq-lite/templates.html#faq-35.13を参照してください 。
C++ では、テンプレートは実際のクラスの単なるメタ定義です。テンプレート化されたクラスをコンパイルすると、コンパイラは、渡された特定のタイプのデータに対して、実際のクラスのコードをオンザフライで実際に生成します (テンプレートはコピーする単なる「パターン」です)。
たとえば、次のコードがある場合
struct MyTemplate
{
private:
float MyValue;
public:
float Get() { return MyValue; }
void Set(float value) { MyValue = value; }
};
void main()
{
MyTemplate v1;
MyTemplate v2;
v1.Set(5.0f);
v2.Set(2);
v2.Get();
}
コンパイラが実際に見ているのは
struct CompilerGeneratedNameFor_MyTemplate_float
{
private:
float MyValue;
public:
float Get() { return MyValue; }
void Set(float value) { MyValue = value; }
};
struct CompilerGeneratedNameFor_MyTemplate_int
{
private:
int MyValue;
public:
int Get() { return MyValue; }
void Set(int value) { MyValue = value; }
};
void main()
{
CompilerGeneratedNameFor_MyTemplate_float v1;
CompilerGeneratedNameFor_MyTemplate_int v2;
v1.Set(5.0f);
v2.Set(2);
v2.Get();
}
おそらくおわかりのように、コンパイラは、テンプレートのインスタンスを実際に宣言するまで、生成するコードを実際には知りません。これは、テンプレートが実際に最終的にどうなるかわからないため、テンプレートをライブラリにコンパイルできないことを意味します。これに関する良いニュースは、テンプレート定義を含むヘッダー ファイルを配布するだけであれば、実際にはライブラリをコンパイルまたはインクルードする必要がないことです。
また、補足として、「#include」プリコンパイラ コマンドは、実際には「#include」をそのファイルのすべてのものに置き換えるようにプリコンパイラに指示するだけです。
すべてのコードが .h ファイルにある場合、コードを使用するためにスタティック ライブラリをコンパイルする必要はありません。
すべてのコードはコンパイル時にライブラリで使用できるため、リンク時には何も必要ありません。
ライブラリがすべてヘッダーファイルに実装されている場合、それを使用するためにバイナリをビルドする必要はありません。そうは言った。私は通常、ヘッダーのみのライブラリの初期開発段階で.cppファイルを作成します。なんで?コンパイラーは、実際に使用されるまで、テンプレートをコンパイルまたは解析しようとはしません。.cppファイルがあり、テンプレートをインスタンス化するためのダミーコードがあると、開発の早い段階で構文エラーを見つけるのに役立ちます。だから私はいくつかのテンプレートコードを追加し、コンパイルを押し、構文エラーを修正し、さらにコードを追加し、コンパイルすることができます...など。数百行のコードを追加した後、愚かな構文エラーを探し出そうとすると、私が何を意味するのかがわかります。ライブラリで単体テストの準備ができたら、.cppファイルを削除し、単体テストを使用して開発を推進します。
また、VC ++を使用してコードをコンパイルするだけの場合、注意する必要があるのは、VC++が実際に使用されるまですべてのテンプレートメンバー関数をコンパイルしようとしないことです。例えば:
template <typename T>
class MyTemplate
{
public:
MyTemplate() {} // default constructor
MyTemplate(int) {
1 = 2
// other syntax error code here
}
};
void f() { MyTemplate<int> myt(); } // compile fine in VC
void g() { MyTemplate<int> myt(1); } // syntax error
f()はVC ++ 2003で問題なくコンパイルされ、g++は構文エラーをキャッチします。VC8とVC9にも同じ問題があると思います。
You're trying to create something unnecessary. Most C libraries (and all C++ libraries) get distributed as two portions:
foo.h
)foo.lib
)For C++ template code, all of your library must be compiled by your end-users, because that's how templates work. There's no reason to provide a pre-compiled library. In this case, your can think of your library distribution as this:
foo.h
)foo-inl.h
)As Niel said above, it's useful to have implementations just for your own testing purposes, and it's probably worthwhile distributing those with the library itself. So you should have a separate suite of unit tests that exercise your code; but those tests shouldn't be part of the library itself.
標準テンプレート ライブラリについて考えてみてください。テンプレート化されたクラスは、別のプロジェクトで使用するときにコンパイルされます。
テンプレートがライブラリにコンパイルされていないことに関して、他の人が言ったことは真実です。ただし、少なくとも構文がチェックされるため、(.cpp ファイルにそれらを #include することによって) コンパイラに表示されるようにすることは、依然として十分に価値があります。
すべてのクラスがテンプレートである場合は、.lib を生成する必要はありません。[1] を配布する .lib を持っていないブーストまたは stlport を見てください。
テンプレートは使用時にコンパイルされます。
[1] 厳密には、正規表現や iostream などのより高度な機能のライブラリを配布していますが、補助ライブラリは他のテンプレートで使用されており、テンプレート自体はライブラリ形式で配布されていません。