The traditional way to achieve this would be to put the source code for each module in a separate directory. Each directory can contain all the source and header files for the module.
An open header for each module can be placed in a separate common header directory. I would probably use a symbolic link from the shared directory to the appropriate module directory for each header.
The compilation rules simply state that no module can include headers from modules other than headers in a shared directory. This leads to the fact that no module can include headers from another module, except for the public header (thus, using private barriers).
Preventing circular dependencies is not automatically trivial. The problem is that you can only establish that there is a circular dependency by looking at several source files at a time, and the compiler only looks one at a time.
Consider a couple of modules ModuleA and ModuleB and program1, which uses both modules.
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
When compiling Program1.c, it is perfectly legal for him to include both ModuleA.h and ModuleB.h if he uses the services of both modules. Thus, ModuleA.h cannot complain if ModuleB.h is included in the same translation unit (TU), and not one of ModuleB.h modules can complain if ModuleA.h is included in the same TU.
Assume that it is legal to use ModuleB facilities for ModuleA. Therefore, when compiling ModuleA1.c or ModuleA2.c, there can be no problem with the inclusion of modules Module.h and ModuleB.h.
However, to prevent circular dependencies, you should be able to prohibit the use of ModuleB1.c and ModuleB2.c code with ModuleA.h.
As far as I can tell, the only way to do this is by some method that requires a private header for ModuleB that says “ModuleA is already enabled”, although it is not, and it is included before ModuleA.h when- either included.
The skeleton ModuleA.h will be the standard format (and ModuleB.h will be similar):
#ifndef MODULEA_H_INCLUDED #define MODULEA_H_INCLUDED ...contents of ModuleA.h... #endif
Now, if the code in ModuleB1.c contains:
#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...
This is far from automatic.
You can analyze the included files and require that it contains non-point topological dependency types. Previously, on UNIX systems (and the accompanying lorder program), the tsort program was tsort , which together provided the necessary services so that you could create a static ( .a ) library containing object files in order that did not require re-scanning the archive. The ranlib program, and ultimately ar and ld took on the responsibility of managing the rescan of one library, thereby making lorder particularly redundant. But tsort has a more general use; it is available on some systems (e.g. MacOS X, RHEL 5 Linux).
So, using GCC dependency tracking plus tsort , you should be able to check if loops between modules exist. But this will need to be handled with some caution.
There may be some IDE or other toolkit that processes this material automatically. But, as a rule, programmers can be disciplined enough to avoid problems - provided that the requirements and intermodule dependencies are carefully documented.