14

ディレクティブを使用using namespaceして識別子/変数名の衝突を回避できますが、大規模なプロジェクトでファイル名またはライブラリ名の衝突が発生するとどうなりますか。

従来のアプローチでは、ディレクティブCを使用してファイルを再帰的に追加します。ディレクティブを使用せずに同じことを達成し、アプリケーションと共有ライブラリ間でファイル名が重複する問題に対処#include_nextするにはどうすればよいですか。「class」という名前の識別子と衝突する AIX math.h の class() 関数の回避策の例。C++#include_next

/* GNU Lesser GPLv2.1 or later */
#ifndef FFMPEG_COMPAT_AIX_MATH_H
#define FFMPEG_COMPAT_AIX_MATH_H

#define class some_text

#include_next <math.h>

#undef class

#endif /* FFMPEG_COMPAT_AIX_MATH_H */

編集: たとえばclass machine-instruction-set、バイナリを複数のプラットフォームで実行する必要がある場合に使用できますか? そのような場合、名前空間の衝突はありますか?

4

6 に答える 6

28

名前空間ディレクティブを使用して、識別子/変数名の衝突を回避できます

まったく逆に、using namespaceディレクティブは衝突を引き起こします。std::vector<>vs.などのスコープを指定することで衝突を解決しますboost::numeric::ublas::vector<>

...しかし、大規模なプロジェクトでファイル名またはライブラリ名の衝突が発生するとどうなりますか?

ファイル名の衝突は、体系化することで簡単に防ぐことができます。名前空間を模倣するようにヘッダーを整理します。たとえばboost::numeric::ublas::vector<>#include <boost/numeric/ublas/vector.hpp>. また、異なるディレクトリ プレフィックスを使用して同じ名前のヘッダーを含めることができるように、異なるライブラリのヘッダーとソースを 1 つのディレクトリに積み上げないで#include <lzma/version.h>ください#include <linux/version.h>

于 2013-09-30T13:44:32.827 に答える
7

これに対処する正しい方法は、名前の競合が発生しにくくなるようにビルド環境をセットアップすることです。

ライブラリ作成のガイドライン

たとえば、ほとんどのサードパーティ ライブラリは、プレーン ファイルをコンパイラのインクルード パスに導入しません。代わりに、ファイルを含むディレクトリを導入します。さまざまなモジュールにサブディレクトリを導入することで、柔軟性を高めることができます。

Boostのディレクトリ構造を考えてみましょう。トップレベルでは、Boost はインクルード検索パスに 1 つの名前のみを導入します:boostディレクトリ。boostそうすれば、衝突する可能性が高い多くのヘッダー ファイル名 ( array.hppthread.hpp、または など) が導入されていてもfunction.hpp、それらはすべてサブディレクトリにラップされます。

#include <boost/thread.hpp> // this is boost's header
#include "thread.hpp"       // some header specific to my current project
                            // no name clash :)

Boost に同梱されているさまざまなライブラリにも同じ概念が使用されています。たとえば、Boost lockfree と Boost assign の両方にqueue.hppヘッダーがあります。しかし、それらは異なるサブディレクトリにあるため、衝突はありません:

#include <boost/lockfree/queue.hpp>
#include <boost/assign/std/queue.hpp>   // no clash :)

適切なヘッダー ファイルを簡単に見つけられるようにするために、Boost はインクルード ファイルと名前空間に同じ構造を使用します。ロックフリー キューはboost::lockfree名前空間に存在し、割り当てキュー ヘッダーの関数は に移動しboost::assignます。そうすれば、一致する名前空間をインクルード ファイルから簡単に見つけられるだけでなく、名前空間の衝突の可能性も減少します。これは、名前空間の衝突がファイル レイヤーでの物理名の衝突として現れる可能性があるためです。

これらのガイドラインを独自のプロジェクトに適応させることができます

  • そのままのインクルード ファイルをインクルード パスに直接導入しないでください。代わりに、インクルード パスが名前で汚染されないように、それらをディレクトリにグループ化します。
  • ディレクトリ ツリーを使用して、単一のライブラリ内のモジュールのインクルード ファイルをさらに構造化します。
  • 物理構造と論理構造を一致させてください。ファイルシステムのインクルード パスは、可能であれば名前空間の階層に似ている必要があります。

これにより、そもそもほとんどの名前衝突が回避されます。問題は、これらのルールに従わないサードパーティのライブラリを使用する必要があり、制御できない衝突が発生した場合はどうなるかということです。

サードパーティ ライブラリによる名前の衝突に対処するためのガイドライン

答えは残忍であり、ビルド環境を通じて分離を強制することです。競合するライブラリを一意に識別可能なサブディレクトリに移動してインクルード パスを再編成し、物理的な競合を解決します。これは通常、重要ではありません。論理的な競合が発生すると、パッチを適用して再コンパイルする必要があり、これはさらに不便です。しかし、ここで実際に名前の競合が発生した場合は、ライブラリ ベンダーの少なくとも 1 つがうまく機能していないことを示しており、バグを報告することを検討する必要があります。

物理的な衝突を修正するようなアドホックな修正#include_nextや、論理的な衝突を修正するためのプリプロセッサの定義には近づかないでください。それらは汚いハックであり、一時的に問題を解決するかもしれませんが、最終的に戻ってきて噛む可能性が非常に高くなります.

于 2013-09-30T14:05:14.413 に答える
4

ファイル名の衝突

ライブラリを別々のサブディレクトリに配置し、親ディレクトリを検索場所として設定します。したがって、代わりに:

#include "zerz.h"  // supposed to be from libfoo
#include "zerz.h"  // supposed to be from libbar

あなたはこれを行うことができます:

#include "libfoo/zerz.h"
#include "libbar/zerz.h"

識別子の衝突

pimpl イディオムを使用して各ライブラリとやり取りするコードを分離し、推移的なインクルードが原因で競合する識別子が無数のプロジェクトに引きずり込まれないようにします。

たとえば、libfoo と libbar の両方に という関数がある場合Frobnicate、これらのライブラリに依存するすべてのコードを分離して、それらのライブラリのヘッダーを他に含める必要がなく、競合が発生しないようにする必要があります。実際に呼び出す .cpp (または .c) ファイルのみがFrobnicate、ライブラリのヘッダー ファイルを #include する必要があります。Frobnicateこれにより、意図しない推移的なインクルードが防止されます。これは、通常、単一のコンパイル単位に含まれているという競合する宣言が発生する方法です。

pimpl イディオムは通常 C++ の用語で示されますが、C でも同じゲームをプレイできます。ポイントは、ヘッダーでライブラリに独自の API を定義することです。API は同じ機能を持つ必要がありますが、競合しない名前を使用する必要があります (名前空間を使用できない場合は、一意のプレフィックスを追加するなど)。すべてのコードは、競合することなくそのヘッダーをインクルードできる必要があります。次に、実装ファイル (.cpp または .c) を提供します。これは、実際のライブラリ ヘッダーを #include する唯一のファイルです。この実装ファイルは基本的に、プレフィックス付き関数の呼び出しを実際のライブラリ関数に転送します。あなたがしなければならないことは、実行可能なはずのこの 1 つのファイルで衝突を避けることだけです。

于 2013-09-30T14:04:37.003 に答える
2

まず、#include_next標準ではなく、gcc 拡張機能であり、Clang でもサポートされていますが、他のコンパイラがサポートしているかどうかはわかりません。

ファイル名の衝突に対処する唯一の方法は、#include "foo.h"またはを適切に使用すること#include <foo.h>です。前者は現在のプロジェクトに対してローカルであることを意図しており、後者はシステム ライブラリに使用されます。通常#include "foo.h"、システム ライブラリも検索しますが、その逆は検索しません。

とにかく、#include "foo.h"ソース ファイル ディレクトリを調べることから始めてください。etc を使用#include "../foo.h"して相対インクルードを行うことができます。同じプロジェクト内でファイル名の競合がある場合は、異なるコンパイル フラグを使用して異なる検索パスを設定する必要があります (基本的にはサブプロジェクトを作成します)。

シンボルの競合については、#definebefore#includeが標準的な方法です。

もちろん、最善の方法は、そもそもこの種の問題を回避するように注意することです。

于 2013-09-28T13:22:29.783 に答える
1

C++ プログラムで C インクルードを使用するときに使用する便利な方法は、.h ファイルで次のコード スニペットを使用することです。

#ifdef __cplusplus
extern "C" {
#endif

<your code here...>

#ifdef __cplusplus
}
#endif

これにより、C++ オブジェクトが C オブジェクトとリンクできるようになります。他の方法についてはわかりませんが、おそらくこれは部分的な答えです。

于 2013-10-04T19:39:31.190 に答える
0

「ディレクティブを使用using namespaceして、識別子/変数名の衝突を回避できます」: いいえ! 名前の衝突を避けるために、そのディレクティブの使用を避ける必要があります。

于 2014-08-15T17:45:17.323 に答える