7

一般的な質問:

アンマネージ C++ の場合、内部コード共有に適しているのはどれですか?

  1. 実際のソース コードを共有してコードを再利用しますか? また
  2. ライブラリ/動的ライブラリ (+ すべてのヘッダー ファイル) を共有してコードを再利用する

コードの重複 (コピーアンドペースト症候群) やコードの肥大化を減らすための戦略は何ですか?


具体例:

私の組織でコードを共有する方法は次のとおりです。

実際のソース コードを共有することで、コードを再利用します。

プロジェクトは実際にはクロスプラットフォームである必要がありますが、VS2008 を使用して Windows で開発しています。多くのプロジェクト (.vcproj) がリポジトリにコミットされています。独自のリポジトリを持つものもあれば、リポジトリの一部になるものもあります。成果物ソリューション (.sln) (たとえば、顧客に提供するもの) ごとに、リポジトリから必要なすべてのプロジェクト (.vcproj) を svn:externals して「最終」製品を組み立てます。

これは問題なく動作しますが、最終的に各ソリューションのコード サイズが非常に大きくなる可能性があることを非常に心配しています (現在、合計コード サイズは約 75K SLOC です)。

また、注目すべきことの 1 つは、推移的な依存関係をすべて防止することです。つまり、実際のソリューション (.sln) ではない各プロジェクト (.vcproj) は、依存している場合でも、他のプロジェクトを svn:externals することはできません。これは、同じライブラリ (つまり Boost) またはプロジェクト (.vcproj) に依存する可能性のある 2 つのプロジェクト (.vcproj) を持つ可能性があるためです。したがって、両方のプロジェクトを svn:externals で 1 つのソリューションにすると、svn:externals はそれを 2 回実行します。 . そのため、各プロジェクトのすべての依存関係を慎重に文書化します。ソリューション (.sln) を作成する担当者は、すべての依存関係 (推移的を含む) がソリューションの一部として svn:externals であることを確認します。

代わりに.lib 、 .dll を使用してコードを再利用すると、明らかに各ソリューションのコード サイズが縮小され、該当する場合は上記の推移的な依存関係が排除されます (例外は、たとえば、使用するサード パーティのライブラリ/フレームワークです)。 dll (Intel TBB やデフォルトの Qt など)


補遺: (必要に応じて読んでください)

ソース コードを共有するもう 1 つの動機は、 Dr. GUIによって最もよく要約されている可能性があります。

その上、C++ で簡単にできるのは、再利用可能なバイナリ コンポーネントを作成することではありません。むしろ、C++ はソース コードの再利用を比較的容易にします。ほとんどの主要な C++ ライブラリは、コンパイルされた形式ではなく、ソース形式で出荷されることに注意してください。オブジェクトから正しく継承するために、そのソースを確認することが非常に頻繁に必要になります。また、元のライブラリを再利用するときに、元のライブラリの実装の詳細に依存するのは非常に簡単です (多くの場合必要になります)。それだけでは不十分であるかのように、元のソースを変更してライブラリのプライベート ビルドを実行したくなる (または必要になる) ことがよくあります。(MFC のプライベート ビルドはいくつありますか? 世界は決して知りません . . .)

おそらくこれが、Intel Math Kernel ライブラリのようなライブラリを見ると、"lib" フォルダに、Visual Studio バージョンごとに "vc7"、"vc8"、"vc9" がある理由です。怖いもの。

または、次の主張はどうでしょうか

C++ は、プラグインに関しては対応が難しいことで有名です。C++ は、プラットフォーム固有およびコンパイラ固有です。C++ 標準ではアプリケーション バイナリ インターフェイス (ABI) が指定されていません。つまり、異なるコンパイラの C++ ライブラリや、同じコンパイラの異なるバージョンであっても互換性がありません。それに加えて、C++ には動的ローディングの概念がなく、各プラットフォームが独自のソリューション (他のプラットフォームと互換性がない) を提供しており、全体像がわかります。

上記の主張についてどう思いますか。Java や .NET などは、この種の問題に直面していますか? たとえば、Netbeans から JAR ファイルを生成する場合、それを IntelliJ にインポートすると、両方に互換性のある JRE/JDK があることが保証されていれば機能しますか?

4

4 に答える 4

6

C は ABI を指定すると人々は考えているようです。そうではありませんし、そうする標準化されたコンパイル済み言語を私は知りません。あなたの主な質問に答えるために、ライブラリの使用はもちろん行くべき道です - 私は他に何かをすることは想像できません.

于 2009-12-11T22:21:03.653 に答える
3

ソース コードを共有する正当な理由の 1 つ: テンプレートは C++ の最高の機能の 1 つです。テンプレートは静的型付けの硬直性を回避するエレガントな方法ですが、その性質上、ソース レベルの構成要素です。ソース レベルのインターフェイスではなく、バイナリ レベルのインターフェイスに注目すると、テンプレートの使用が制限されます。

于 2009-12-11T22:59:37.507 に答える
1

私たちも同じです。異なるプラットフォーム、ビルド環境で共有コードを使用する必要がある場合、または C ランタイムへの静的リンクと動的リンク、異なる構造パッキング設定などの異なるビルド オプションが必要な場合でも、バイナリを使用しようとすると、実際の問題になる可能性があります。 .

私は通常、zlib や libpng などのサードパーティ コードを使用する場合でも、可能な限りオンデマンドでソースからビルドするようにプロジェクトを設定します。Boost など、個別にビルドする必要があるものについては、通常、必要な設定のさまざまな組み合わせ (デバッグ/リリース、VS7.1/VS9、静的/動的) に対して 4 つまたは 8 つの異なるバイナリ セットをビルドし、それらを管理する必要があります。バイナリとソース管理のデバッグ情報ファイル。

もちろん、コードを共有する全員が同じツールを同じプラットフォームで同じオプションで使用している場合は、話は別です。

于 2009-12-11T22:52:44.557 に答える
1

古いプロジェクトのコードを新しいプロジェクトに再利用する方法として、共有ライブラリを見たことがありません。肥大化を最小限に抑えるために、ほぼ同時に開発しているさまざまなアプリケーション間でライブラリを共有することが重要だといつも思っていました。

コピー アンド ペースト シンドロームに関する限り、2 つ以上の場所にコピー アンド ペーストする場合、それは独自の機能である必要があります。これは、ライブラリが共有されているかどうかに関係ありません。

古いプロジェクトのコードを再利用するときは、常にソースとして取り込みます。微調整が必​​要なものは常にあり、通常は、前のプロジェクトを壊してしまう可能性のある共有バージョンを微調整するよりも、プロジェクト固有のバージョンを微調整する方が安全です。以前のプロジェクトに戻って修正することは問題外です。なぜなら、1) すでに機能している (そして出荷されている)、2) 資金が提供されていない、3) 必要なテスト ハードウェアが利用できない可能性があるからです。

たとえば、ソケットやパイプなどを介して、メッセージ ID を持つデータのブロックである「メッセージ」を送信するための API を備えた通信ライブラリがありました。

void Foo:Send(unsigned messageID, const void* buffer, size_t bufSize);

しかし、その後のプロジェクトでは、最適化が必要でした。メッセージは、連結されたメモリのさまざまな部分にある複数のデータ ブロックで構成される必要があり、ポインタ演算を実行して作成することはできませんでした (とにかくやりたくなかったのです)。そもそも「組み立てられた」形式のデータであり、パーツをまとめて統合バッファーにコピーするプロセスに時間がかかりすぎていました。そこで、新しい API を追加しました。

void Foo:SendMultiple(unsigned messageID, const void** buffer, size_t* bufSize);

バッファをメッセージに組み立てて送信します。(基本クラスのメソッドは、一時バッファを割り当て、パーツをまとめてコピーし、 を呼び出しFoo::Send()ました。サブクラスは、これをデフォルトとして使用することも、独自のものでオーバーライドすることもできます。たとえば、ソケットでメッセージを送信したクラスは、それぞれに対して send() を呼び出すだけです。バッファを作成し、大量のコピーを排除します)。

これを行うことで、変更を古いバージョンにバックポート (実際にはコピー) するオプションがありますが、バックポートする必要はありません。これにより、マネージャーは、時間と資金の制約に基づいて柔軟に対応できます。

編集: ニールのコメントを読んだ後、明確にする必要があることを考えました。

私たちのコードでは、多くの「ライブラリ」を実行しています。それらの多くは。私が書いた 1 つの大きなプログラムには、50 個ほどのプログラムがありました。私たちにとって、そして私たちのビルド設定では、それらは簡単だからです。

私たちは、依存関係とほとんどすべてを処理しながら、オンザフライでメイクファイルを自動生成するツールを使用しています。何か奇妙なことが必要な場合は、通常は数行だけの例外を含むファイルを作成します。

このツールは、ディレクトリ内でソース ファイルのように見えるものをすべて見つけ、ファイルが変更された場合は依存関係を生成し、必要なルールを吐き出します。次に、すべてを取得し、ディレクトリにちなんで名付けられた libxxx.a ファイルに ar/ranlib するルールを作成します。すべてのオブジェクトとライブラリは、ターゲット プラットフォームにちなんで名付けられたサブディレクトリに配置されます (これにより、クロス コンパイルのサポートが容易になります)。このプロセスは、すべてのサブディレクトリ (オブジェクト ファイルのサブディレクトリを除く) に対して繰り返されます。次に、最上位ディレクトリがすべてのサブディレクトリのライブラリとリンクされて実行可能ファイルになり、シンボリックリンクが最上位ディレクトリの後にネイキッドで再び作成されます。

したがって、ディレクトリはライブラリです。プログラムでライブラリを使用するには、ライブラリへのシンボリック リンクを作成します。無痛。したがって、最初からすべてがライブラリに分割されています。共有ライブラリが必要な場合は、ディレクトリ名に「.so」サフィックスを付けます。

別のプロジェクトからライブラリを取得するには、Subversion 外部を使用して必要なディレクトリをフェッチするだけです。シンボリック リンクは相対的なので、何かを残さない限りは機能します。出荷時には、外部参照を親の特定のリビジョンにロックします。

ライブラリに機能を追加する必要がある場合は、いくつかの方法のいずれかを実行できます。親を改訂し (まだアクティブなプロジェクトであり、テスト可能である場合)、Subversion に新しいリビジョンを使用するように指示し、ポップアップするバグを修正します。または、親をいじるのが危険すぎる場合は、コードを複製して外部リンクを置き換えることができます。いずれにせよ、私たちにはまだ「図書館」のように見えますが、図書館の精神に合っているかどうかはわかりません。

「外部」メカニズムを持たない Mercurial への移行を進めているため、最初にライブラリを複製するか、rsync を使用して異なるリポジトリ間でコードの同期を維持するか、共通のディレクトリ構造を強制する必要があります。複数の親から hg プルを取得できます。最後のオプションはかなりうまく機能しているようです。

于 2009-12-11T22:53:44.917 に答える