web-dev-qa-db-ja.com

ヘッダーのみのライブラリの利点

ヘッダーのみのライブラリの利点は何ですか?また、実装を別のファイルに入れることに反対する方法でそれを書くのはなぜですか?

81
NebulaFox

テンプレートを扱う場合など、ヘッダーのみのライブラリが唯一のオプションである場合があります。

ヘッダーのみのライブラリを使用すると、ライブラリが使用される可能性のある異なるプラットフォームについて心配する必要がなくなります。実装を分離するとき、通常は実装の詳細を隠し、ヘッダーとライブラリの組み合わせとしてライブラリを配布します(libdll 'sまたは.soファイル)。もちろん、これらはサポートを提供するすべての異なるオペレーティングシステム/バージョン用にコンパイルする必要があります。

実装ファイルを配布することもできますが、それはユーザーにとって追加のステップであり、ライブラリを使用する前にコンパイルすることを意味します。

もちろん、これはcase-by-caseに基づいて適用されます。たとえば、ヘッダーのみのライブラリは時々増加します コードサイズ コンパイル時間。

49
Luchian Grigore

ヘッダーのみのライブラリの利点:

  • ビルドプロセスを簡素化します。ライブラリをビルドする必要はなく、ビルドのリンク手順中にコンパイル済みライブラリを指定する必要もありません。コンパイル済みライブラリがある場合は、おそらく複数のバージョンをビルドする必要があります。1つはデバッグを有効にしてコンパイルし、もう1つは最適化を有効にして、さらに別のシンボルを削除します。また、マルチプラットフォームシステムの場合はさらに多くなります。

ヘッダーのみのライブラリの欠点:

  • より大きなオブジェクトファイル。一部のソースファイルで使用されるライブラリのすべてのインラインメソッドも、そのソースファイルのコンパイル済みオブジェクトファイルに、アウトライン定義の弱いシンボルを取得します。これにより、コンパイラが遅くなり、リンカも遅くなります。コンパイラはその膨張をすべて生成する必要があり、その後、リンカーはそれを除外する必要があります。

  • 長いコンパイル。上記の膨張の問題に加えて、ヘッダーはコンパイルされたライブラリよりもヘッダーのみのライブラリの方が本質的に大きいため、コンパイルに時間がかかります。これらの大きなヘッダーは、ライブラリを使用するソースファイルごとに解析する必要があります。もう1つの要因は、ヘッダーのみのライブラリのヘッダーファイルは、インライン定義で必要な#includeヘッダーと、ライブラリがコンパイル済みライブラリとして構築された場合に必要なヘッダーを必要とすることです。

  • より複雑なコンパイル。ヘッダーのみのライブラリでは、余分な#includesが必要なため、ヘッダーのみのライブラリではより多くの依存関係が発生します。ライブラリ内の重要な機能の実装を変更すると、プロジェクト全体を再コンパイルする必要が生じる場合があります。コンパイルされたライブラリのソースファイルを変更し、その1つのライブラリソースファイルを再コンパイルし、コンパイルされたライブラリをその新しい.oファイルで更新し、アプリケーションを再リンクするだけです。

  • 人間にとって読みにくい。最高のドキュメントであっても、ライブラリのユーザーはライブラリのヘッダーを読むことに頼らなければならないことがよくあります。ヘッダーのみのライブラリのヘッダーには、インターフェイスの理解を妨げる実装の詳細が含まれています。コンパイルされたライブラリの場合、表示されるのはインターフェイスと実装の実行内容に関する簡単なコメントだけで、通常はそれで十分です。本当に必要なのはそれだけです。ライブラリの使用方法を知るために、実装の詳細を知る必要はありません。

53
David Hammen

これが古いスレッドであることは知っていますが、ABIインターフェイスや特定のコンパイラの問題については誰も言及していません。だから私はそうするだろうと思った。

これは基本的に、ユーザーに配布するヘッダー付きのライブラリを作成するか、すべてをヘッダーに含めることで自分を再利用するという概念に基づいています。ヘッダーとソースファイルを再利用し、すべてのプロジェクトでこれらを再コンパイルすることを考えている場合、これは実際には適用されません。

基本的に、C++コードをコンパイルして1つのコンパイラーでライブラリをビルドすると、ユーザーはそのライブラリーを異なるコンパイラーまたは同じコンパイラーの異なるバージョンで使用しようとすると、バイナリーの非互換性によるリンカーエラーまたは奇妙なランタイム動作が発生する可能性があります。

たとえば、コンパイラベンダーは、バージョン間でSTLの実装を頻繁に変更します。ライブラリにstd :: vectorを受け入れる関数がある場合、そのクラスのバイトは、ライブラリがコンパイルされたときに配置された方法で配置されると想定されます。新しいコンパイラバージョンで、ベンダーがstd :: vectorの効率を改善した場合、ユーザーのコードは異なる構造を持つ新しいクラスを認識し、その新しい構造をライブラリに渡します。そこからすべてが下り坂になります...これが、ライブラリの境界を越えてSTLオブジェクトを渡さないことが推奨される理由です。 Cランタイム(CRT)タイプにも同じことが当てはまります。

CRTについて話している間、ライブラリとユーザーのソースコードは通常、同じCRTにリンクする必要があります。 Visual Studioでは、マルチスレッドCRTを使用してライブラリを構築しますが、ユーザーがマルチスレッドデバッグCRTにリンクすると、ライブラリが必要なシンボルを見つけられない可能性があるため、リンクの問題が発生します。どの関数だったかは思い出せませんが、Visual Studio 2015では、Microsoftは1つのCRT関数をインラインで作成しました。突然、CRTライブラリではなくヘッダーにあったので、リンク時にそれを見つけることを期待していたライブラリはもはやできなくなり、リンクエラーが発生しました。その結果、これらのライブラリはVisual Studio 2015で再コンパイルする必要がありました。

また、Windows APIを使用しているが、ライブラリユーザーに対して異なるUnicode設定でビルドすると、リンクエラーや奇妙な動作が発生する可能性があります。これは、Windows APIにUnicodeまたはASCII文字列とマクロ/定義を使用する関数があり、プロジェクトのUnicode設定に基づいて自動的に正しいタイプを使用するためです。ライブラリの境界を越えて文字列を渡す場合それは間違った型であり、実行時に問題が発生するか、プログラムが最初にリンクしないことがあります。

これらのことは、他のサードパーティライブラリ(EigenベクトルやGSLマトリックスなど)からライブラリの境界を越えてオブジェクト/タイプを渡す場合にも当てはまります。サードパーティのライブラリが、ライブラリのコンパイルとユーザーのコードのコンパイルの間にヘッダーを変更すると、問題が発生します。

基本的に安全にするために、ライブラリの境界を越えて渡すことができるのは、タイプとプレーンオールドデータ(POD)のみです。理想的には、PODは、独自のヘッダーで定義され、サードパーティのヘッダーに依存しない構造体にする必要があります。

ヘッダーのみのライブラリを提供すると、すべてのコードが同じコンパイラー設定で同じヘッダーに対してコンパイルされるため、これらの問題の多くは解消されます(ユーザーとユーザーが使用する3番目の部分的なライブラリのバージョンはAPI互換です)。

ただし、コンパイル時間の増加など、上記で言及されたマイナス面があります。また、ビジネスを運営している場合があります。そのため、ソースコードの実装のすべての詳細をすべてのユーザーに渡したくない場合があります。

11
Phil Rosenberg

主な「利点」は、ソースコードを提供する必要があることです。そのため、マシンに関するエラーレポートと、聞いたこともないコンパイラーが発生します。ライブラリが完全にテンプレートである場合、多くの選択肢はありませんが、選択肢がある場合、通常、ヘッダーのみがエンジニアリングの選択肢として不適切です。 (もちろん、ヘッダーは、統合手順を文書化する必要がないことを意味するだけです。)

8
James Kanze