ヘッダーのみのライブラリの利点は何ですか?また、実装を別のファイルに入れるのとは反対の方法でそれを書くのはなぜですか?
ヘッダーのみのライブラリの利点は何ですか?また、実装を別のファイルに入れるのとは反対の方法でそれを書くのはなぜですか?
回答:
テンプレートを扱う場合など、ヘッダーのみのライブラリが唯一のオプションである状況があります。
ヘッダーのみのライブラリがあることは、ライブラリが使用される可能性のある異なるプラットフォームについて心配する必要がないことも意味します。あなたは、実装を分離するときは、通常は非表示の実装の詳細にそう、およびヘッダとライブラリの組み合わせとしてライブラリを配布する(lib
、dll
のまたは.so
ファイル)。もちろん、これらはサポートするすべての異なるオペレーティングシステム/バージョン用にコンパイルする必要があります。
実装ファイルを配布することもできますが、これはユーザーが追加の手順を行うことを意味します-使用する前にライブラリをコンパイルすることです。
もちろん、これはケースバイケースで適用されます。たとえば、ヘッダーのみのライブラリは時々増加しますコードサイズ& コンパイル時間。
ヘッダーのみのライブラリの利点:
ヘッダーのみのライブラリの欠点:
より大きなオブジェクトファイル。一部のソースファイルで使用されているライブラリからのすべてのインラインメソッドも、そのソースファイルのコンパイル済みオブジェクトファイルで弱いシンボル、行外定義を取得します。これにより、コンパイラの速度が低下し、リンカの速度も低下します。コンパイラはすべての膨らみを生成する必要があり、リンカはそれを除外する必要があります。
より長いコンパイル。上記の膨らみの問題に加えて、ヘッダーのみのライブラリでは、コンパイルされたライブラリよりもヘッダーが本質的に大きいため、コンパイルに時間がかかります。これらの大きなヘッダーは、ライブラリを使用するソースファイルごとに解析する必要があります。もう1つの要因は、ヘッダーのみのライブラリ内のこれらのヘッダーファイルは#include
、インライン定義で必要なヘッダーと、ライブラリがコンパイル済みライブラリとしてビルドされた場合に必要となるヘッダーでなければならないことです。
よりもつれたコンパイル。ヘッダーのみのライブラリでは追加#include
のが必要になるため、ヘッダーのみのライブラリでは依存関係が大幅に増えます。ライブラリのいくつかの主要な関数の実装を変更すると、プロジェクト全体を再コンパイルする必要が生じる場合があります。コンパイル済みライブラリのソースファイルにその変更を加えれば、その1つのライブラリソースファイルを再コンパイルし、コンパイル済みライブラリをその新しい.oファイルで更新して、アプリケーションを再リンクするだけです。
人間にとって読みにくい。最高のドキュメントがあっても、ライブラリのユーザーは、多くの場合、ライブラリのヘッダーを読む必要があります。ヘッダーのみのライブラリのヘッダーには、インターフェイスの理解を妨げる実装の詳細が含まれています。コンパイルされたライブラリを使用すると、表示されるのはインターフェースと実装の動作に関する簡単な説明だけであり、通常はそれで十分です。それが本当にあなたが望むべきすべてです。ライブラリの使用方法を知るために、実装の詳細を知っている必要はありません。
detail
です。
私はこれが古いスレッドであることを知っていますが、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と互換性がある場合)。
ただし、コンパイル時間の増加など、前述の欠点があります。また、ビジネスを運営している可能性があるため、いずれかのユーザーが盗んだ場合にすべてのユーザーにすべてのソースコード実装の詳細を渡したくない場合があります。
インライン化は、リンク時最適化(LTO)によって実行できます。
ヘッダーのみのライブラリの2つの主な利点の1つである「インラインでヘッダーを定義する必要がある」のいずれかの値が減少するため、これを強調したいと思います。
この最小の具体例は、リンク時の最適化とインラインで示されています。
したがって、フラグを渡すだけで、リファクタリング作業なしでオブジェクトファイル間でインライン化を行うことができ、そのためにヘッダーに定義を保持する必要がなくなります。
ただし、LTOにも独自の欠点がある可能性があります。リンク時最適化(LTO)を使用しない理由はありますか?