Path: utzoo!utgpu!utstat!jarvis.csri.toronto.edu!rutgers!usc!cs.utexas.edu!uunet!ncrlnk!ncratl!ncratl2!dspoon From: dspoon@ncratl2.Atlanta.NCR.COM (Dave Witherspoon) Newsgroups: comp.lang.c++ Subject: Re: A solution to the multiple inclusion problem Message-ID: <1030@ncratl2.Atlanta.NCR.COM> Date: 26 Oct 89 21:13:16 GMT References: <14240@well.UUCP> <1989Oct23.191634.6345@cs.rochester.edu> <950@dutrun.UUCP> Distribution: comp Organization: NCR Corporation, E&M Atlanta Lines: 100 In article <950@dutrun.UUCP>, leo@duttnph.tudelft.nl (Leo Breebaart) writes: > The problem of managing include-files for c++ libraries is also > discussed in the following paper: > > Managing C++ Libraries > James M. Coggins & Gregory Bollella > SIGPLAN Notices, Vol. 24, No. 6 > I did not see Nagle's orignal posting, so I cannot comment on his proposal. However, many of the suggestions I read I have already tried (only to have them fail) and others will not work in the MS-DOS world. There is one solution we have found that works (hint: Leo knows) in *any* environment. Say I have 2 classes, A and B. A contains a B*, and B contains an A*. Thus, we have a circular dependency! One proposal (that I've tried): A.hpp B.hpp ----- ----- #ifndef D_A #ifndef D_B #define D_A #define D_B #include "B.hpp" #include "A.hpp" class A { B* bp;} class B {A* ap;} #endif #endif Now if we compile B.cpp, who does an #include of B.hpp, then an error will emerge. The way it happens is: 1. D_B isn't #defined, so #define it 2. Hop into A.hpp 3. D_A isn't #defined, so #define it 4. Hop into B.hpp (again) 5. D_B *is* #defined (here's the rub...we haven't seen class B yet) 6. Cruise on into class A 7. Hey...what the heck's a B? Bummer...that didn't work. How's about moving the #define of D_A and D_B behind the class right before the #endif...No cigar. Compiling B.cpp again: 1. D_B isn't defined, so... 2. Hop into A.hpp 3. D_A isn't defined, so... 4. Hop into B.hpp 5. D_B isn't defined, so... 6. Hop into etc., etc., etc. Dang. Foiled again. Let's try this: A.hpp B.hpp ----- ----- #ifndef D_A #ifndef D_B #define D_A #define D_B #ifndef D_B #ifndef D_A #include "B.hpp" #include "A.hpp" #endif #endif class A { B* bp;} class B {A* ap;} #endif #endif Compiling B.cpp again, we will see the same result as the first method...by the time we see B* in class A, class B has not yet been defined. #pragma "once" isn't supported in MSC. Preprocessor proposals are frequent but rarely acted upon amongst all the major vendors. Besides, I need something NOW! We have fully implemented Coggins' proposal and it works great. I could explain in detail, but I'll give a brief explanation. There is one file (prelude.hpp) that checks for the #definition of each type of object in the system, and that file guarantees that *all* the necessary .hpp files are #included _AND_ it guarantees that they are #included in the CORRECT ORDER for the specific object being compiled. We have 140 classes, so this method takes all our troubles away. An example: B depends on A and A2, A depends on Z, Z depends on Y. B.d B.hpp B.cpp prelude.hpp --- ----- ----- ----------- #define D_A class B { #define D_B ...magic... #define D_A2 : #include "prelude.hpp" : }; The new one there is B.d. It explicitly declares all of B's first-level dependencies. You have to do this anyway, but this way it is clear and explicit. A.d would mention his Z dependency, and Z.d would mention Y. When you compile B.cpp, prelude.hpp is opened ONCE. D_Y, D_Z, D_A, D_A2, and D_B are found to be #defined, and their respective .hpp files are #included IN THAT ORDER. Each .hpp file is opened precisely ONCE. Sorry to take so much space. If you want more info, contact me. Counsel rests. -------------------------------David Witherspoon------------------------------- D.Witherspoon@Atlanta.NCR.COM | "Dolphins find humans amusing, but NCR Retail Systems Development/Atlanta| they don't want to talk to them." MY OPINIONS...ALL MINE!!! | - David Byrne