これを実現する従来の方法は、各モジュールのソースコードを別々のディレクトリに配置することです。各ディレクトリには、モジュールのすべてのソースファイルとヘッダーファイルを含めることができます。
各モジュールのパブリックヘッダーは、ヘッダーの個別の共通ディレクトリに配置できます。おそらく、各ヘッダーの共通ディレクトリから関連するモジュールディレクトリへのシンボリックリンクを使用します。
コンパイルルールは、共通ディレクトリ内のヘッダーを除いて、他のモジュールからのヘッダーをモジュールに含めることはできないと単純に述べています。これにより、パブリックヘッダーを除いて、別のモジュールのヘッダーをモジュールに含めることができないという結果が得られます(したがって、プライベートバリアが適用されます)。
循環依存を自動的に防ぐことは簡単ではありません。問題は、一度に複数のソースファイルを調べることによってのみ循環依存関係があることを確認でき、コンパイラーは一度に1つしか調べないことです。
モジュールのペアであるModuleAとModuleB、および両方のモジュールを使用するプログラムであるProgram1について考えてみます。
base/include
ModuleA.h
ModuleB.h
base/ModuleA
ModuleA.h
ModuleA1.c
ModuleA2.c
base/ModuleB
ModuleB.h
ModuleB1.c
ModuleB2.c
base/Program1
Program1.c
Program1.cをコンパイルするとき、両方のモジュールのサービスを利用する場合、ModuleA.hとModuleB.hの両方を含めることは完全に合法です。したがって、ModuleB.hが同じ変換ユニット(TU)に含まれている場合、ModuleA.hは文句を言うことができません。また、ModuleA.hが同じTUに含まれている場合、ModuleB.hも文句を言うことはできません。
ModuleAがModuleBの機能を使用することは合法であると仮定しましょう。したがって、ModuleA1.cまたはModuleA2.cをコンパイルする場合、ModuleA.hとModuleB.hの両方が含まれていても問題はありません。
ただし、循環依存を防ぐには、ModuleB1.cおよびModuleB2.cのコードがModuleA.hを使用することを禁止できる必要があります。
私が見る限り、これを行う唯一の方法は、ModuleA.hが含まれる前に含まれているにもかかわらず、「ModuleAはすでに含まれている」というModuleBのプライベートヘッダーを必要とするいくつかの手法です。
ModuleA.hのスケルトンは標準形式になります(そしてModuleB.hも同様になります)。
#ifndef MODULEA_H_INCLUDED
#define MODULEA_H_INCLUDED
...contents of ModuleA.h...
#endif
ここで、ModuleB1.cのコードに次のものが含まれている場合:
#define MODULEA_H_INCLUDED
#include "ModuleB.h"
...if ModuleA.h is also included, it will declare nothing...
...so anything that depends on its contents will fail to compile...
これは自動にはほど遠いです。
含まれているファイルの分析を行うことができ、依存関係のループのないトポロジカルソートが存在する必要があります。以前はtsort
、UNIXシステム上にプログラム(およびコンパニオンプログラム)があり、アーカイブの再スキャンを必要としない順序でオブジェクトファイルを含むlorder
静的()ライブラリを作成できるように、必要なサービスを一緒に提供していました。.a
プログラムはranlib
、最終的には単一のライブラリの再スキャンを管理する役割ar
をld
引き受けたためlorder
、特に冗長になりました。しかしtsort
、より一般的な用途があります。一部のシステムで使用できます(たとえば、MacOS X、RHEL 5 Linuxも)。
したがって、GCC plusの依存関係追跡を使用するとtsort
、モジュール間にサイクルがあるかどうかを確認できるはずです。しかし、それは注意して処理する必要があります。
このようなものを自動的に処理するIDEまたは他のツールセットがあるかもしれません。しかし、通常、プログラマーは、要件とモジュール間の依存関係が注意深く文書化されている限り、問題を回避するのに十分な訓練を受けることができます。