Recursive #includes

Wayne A. Throop throopw at agarn.dg.com
Sat Mar 4 02:30:00 AEST 1989


> dff at Morgan.COM (Daniel F. Fisher)
>> throopw at agarn.dg.com (Wayne A. Throop)
>>( This scheme still doesn't deal with cyclic includes broken with
>>  #ifdef... but despite there being ways to deal with that as well,
>>  my personal feeling is "don't DO that" is a good remedy.  )

>  it is likely one will [...] need to cope with cyclic
> includes.  That is unless one does away with modularity by putting
> everything in the same include file.

Daniel's examples of this that follow are quite good, but there are
usually palatable alternatives to actually having a.h include b.h
include a.h (breaking the cycle purely by preprocess-time defines).
I'll try to explain these palatable (at least to me) alternatives.  I
emphasize again: there are indeed cases where recursive includes are a
very VERY attractive solution, but there are practically always
alternatives that are at least palatable if not ideal.

> But if the system cannot be
> layered, say due to a reflexive relationship between two modules
> (I point to your objects and you point to mine), this implies that
> there must be cyclic dependencies.

Note that there must be a cyclic dependency only if the INTERFACE to
one module needs to know about the interface to another and vice
versa.  Structs containing pointers to each other is a good example of
this, but there are at least two ways of arranging things in such a
way that cycles do not arise.  The first, as Daniel suggests, is to
define both types in a single header used by both modules.  This
implies that there is one such "buddy" header for (more or less) each
pair of intertwined modules.  Palatable in some cases, but in general
only as a last resort, I suppose.

But note that each module only needs to be able to declare a pointer
to the other's type.  In C, this can be done using incompleted types.
(See section 3.5.2.3 of pANS, and especially footnote 48 of that
section.)  The header file for the first module would export only the
fact that it will implement a struct, and provide the name and the
pointer type for it.  Similarly for the other module.  Then in the
implementation (the .c files), these structs can be defined in the
peace privacy and privacy of their own namespace, with no cyclic
includes.  This is the prefered way to do it, I'd say.  If it is the
case that the two modules need to export the internal details of their
structs, this can be done by having an interface1-level include file
which is to be included by "buddy" modules, and an interface2-level
include file which is to be included by other client modules.

Note that this second method of dealing with reflexive structs is
fully general, does not force "buddy" modules to get into bed together
in a common include file, and effectively gets rid of those annoying
cycles.

> As an example, consider a module called tran.c, that handles a data
> structure defining transactions in a system and trproc.c, that handles
> a data structure defining transaction processors in the same system.
> [...etc...]

I'll rewrite the example and present my alternative below.  Note that
the #ifndef stuff is now not needed to break cycles, but I routinely
use it anyway, to painlessly order nested includes and insure single
inclusion.  Interestingly enough, this rewriting is, in fact, more
modular than when we started, since neither module is privy to the
internals of the other's structs, and a typedef encapsulates the pointer
creation, so that reimplementing either one as (say) a table index
instead of a pointer does not require touching code in the other module.

tran.h:
-------------------------------------
#ifndef included_tran_h
#define included_tran_h

typedef struct tran *tran_t;
...
#endif
-------------------------------------

tran.c:
-------------------------------------
#include "trproc.h"
#include "tran.h"

struct tran
{
	trproc_t frontend_proc;
	trproc_t backend_proc;
...
};
...
-------------------------------------

trproc.h:
-------------------------------------
#ifndef included_trproc_h
#define included_trproc_h

typedef struct trproc *trproc_t;
...
#endif
-------------------------------------

trproc.c:
-------------------------------------
#include "tran.h"
#include "trproc.h"

struct trproc
{
	tran_t *trans;
...
};
...
-------------------------------------

--
Down, down in the basement we hear the sound of machines.
I, I, I'm driving in circles.  Come to my senses sometimes.
Why--why--why start it over?
                                        --- Talking Heads
--
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw



More information about the Comp.lang.c mailing list