Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!rutgers!mcnc!rti!xyzzy!agarn!throopw From: throopw@agarn.dg.com (Wayne A. Throop) Newsgroups: comp.lang.c Subject: Re: Recursive #includes Keywords: recursive includes, modularity Message-ID: <3804@xyzzy.UUCP> Date: 3 Mar 89 16:30:00 GMT References: <570@marob.MASA.COM> <9727@smoke.BRL.MIL> <964@philmds.UUCP> <9736@smoke.BRL.MIL> <3701@xyzzy.UUCP> <237@mstan.Morgan.COM> Sender: usenet@xyzzy.UUCP Lines: 119 > dff@Morgan.COM (Daniel F. Fisher) >> throopw@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 !mcnc!rti!xyzzy!throopw