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


98

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


主にテンプレートですが、配布と使用が少し簡単になります。
BoBTFish、

4
ヘッダーのみのライブラリの欠点を質問の範囲に追加したいと思います...
moooeeeep

まだ言及されていない欠点は何ですか?
NebulaFox

7
@moooeeeep:マイナス面については、C ++のDosおよびDon'ts ChromiumプロジェクトのWebページの「コードのインライン化を停止する」の段落を読むとよいでしょう。
Mr.C64

回答:


57

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

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

実装ファイルを配布することもできますが、これはユーザーが追加の手順を行うことを意味します-使用する前にライブラリをコンパイルすることです。

もちろん、これはケースバイケースで適用されます。たとえば、ヘッダーのみのライブラリは時々増加しますコードサイズ& コンパイル時間。


6
「ヘッダーのみのライブラリを使用すると、ライブラリが使用される可能性のある異なるプラットフォームについて心配する必要がないことも意味します」:ライブラリを維持する必要がない場合のみ。そうでなければ、それは悪夢であり、あなたが持っている素材で再現したりテストしたりできないバグレポートがあります。
James Kanze、2012年

1
ヘッダーのみのパフォーマンス上の利点について同様の質問をしました。ご覧のとおり、コードサイズに違いはありません。ただし、サンプルのヘッダーのみの実装は7%遅くなりました。stackoverflow.com/questions/12290639/...
Homer6

@ Homer6私にpingしてくれてありがとう。これを実際に測定したことはありません。
Luchian Grigore

1
@LuchianGrigoreどちらかを持っている他の人を見つけることができませんでした。そのため、回答に時間がかかりました。非常に多くの投機的な「コードサイズの増加」と「メモリ消費」のコメントがあります。それがほんの一例であっても、ようやく違いのスナップショットを手に入れました。
Homer6、2012年

@ Homer6。なぜコードサイズが大きくならないのですか?ヘッダーのみのライブラリを使用する複数のライブラリを作成し、アプリがそれらのすべてのライブラリを使用すると仮定すると、単一の共有ライブラリに対してリンクするのではなく、複数のコピーが必要になります。
pooya13

60

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

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

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

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

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

  • よりもつれたコンパイル。ヘッダーのみのライブラリでは追加#includeのが必要になるため、ヘッダーのみのライブラリでは依存関係が大幅に増えます。ライブラリのいくつかの主要な関数の実装を変更すると、プロジェクト全体を再コンパイルする必要が生じる場合があります。コンパイル済みライブラリのソースファイルにその変更を加えれば、その1つのライブラリソースファイルを再コンパイルし、コンパイル済みライブラリをその新しい.oファイルで更新して、アプリケーションを再リンクするだけです。

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


21
最後のポイントは本当に意味がありません。適切なドキュメントには、関数宣言、パラメーター、戻り値など、および関連するすべてのコメントが含まれます。ヘッダーファイルを参照する必要がある場合、ドキュメントは失敗しています。
Thomas

6
@Thomas-最高の専門図書館でさえ、「細かい」ヘッダーを読むことに頼らざるを得ないことがよくあります。実際、いわゆる「細かい」ドキュメントがコードとコメントから抽出された場合、私は通常、ヘッダーを読むのが好きです。コードとコメントは、自動生成されたドキュメントよりも多くを教えてくれます。
David Hammen 2014年

2
最後のポイントは無効です。ヘッダーにはプライベートメンバーの実装の詳細がすでに入力されているため、cppファイルですべての実装の詳細が非表示になるわけではありません。さらに、C#のような言語は本質的に設計上「ヘッダーのみ」であり、IDEは詳細を不明瞭にします(「折りたたむ」)
Mark Lakata

2
@Tomas:そうですね、最後のポイントは完全に偽です。ヘッダーのみのライブラリと同様に、インターフェースと実装を簡単に分離できます。インターフェースヘッダー#includeに実装の詳細を含めるだけです。これが、Boostライブラリに通常、というサブディレクトリ(およびネームスペース)が含まれている理由detailです。
Nemo

4
@トーマス:同意しない。ヘッダーファイルは通常、ドキュメントの最初の場所です。ヘッダーが適切に記述されている場合、外部ドキュメントは必要ありません。
Joel Cornett、2015

14

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

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

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

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

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

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

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

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

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

ただし、コンパイル時間の増加など、前述の欠点があります。また、ビジネスを運営している可能性があるため、いずれかのユーザーが盗んだ場合にすべてのユーザーにすべてのソースコード実装の詳細を渡したくない場合があります。


8

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


0

インライン化は、リンク時最適化(LTO)によって実行できます。

ヘッダーのみのライブラリの2つの主な利点の1つである「インラインでヘッダーを定義する必要がある」のいずれかの値が減少するため、これを強調したいと思います。

この最小の具体例は、リンク時の最適化とインラインで示されています。

したがって、フラグを渡すだけで、リファクタリング作業なしでオブジェクトファイル間でインライン化を行うことができ、そのためにヘッダーに定義を保持する必要がなくなります。

ただし、LTOにも独自の欠点がある可能性があります。リンク時最適化(LTO)を使用しない理由はありますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.