The previous topic about declaring a local instance of a mapped record within a function brought to mind a few tips/observations about managing the confusion that can arise when you have
nested ++includes .
A common situation is that you create a ++include file containing some useful related functions. But since those functions may rely on some common symbols (say, in ashell.def), and maybe on other functions as well (perhaps from the sosfunc: directory in the SOSLIB), in order to make your ++include file self-sufficient, you include within it these other ++include files. Good idea, so far. But some of those ++include files may, in turn, include other files (possibly, but hopefully not even including the file that included it, creating an infinite loop). Even without the infinite loop, it makes for complexity, confusion, and complicated compile errors.
So here are a few tricks and techniques that may help:
1. Use
++include\'once to avoid accidentally including the same file multiple times (assuming you don't really intend that). The problem here is that the technique doesn't really work unless you use it consistently everywhere, so it's hard to get started on it.
2. Use
++ifndef conditional compilation directives within your include files, to prevent them from being included multiple times. We use this technique in many of the common ashinc: def files. For example:
++ifndef INC_XTREE_SDF
define INC_XTREE_SDF = 1
...
DEFSTRUCT XTRCTL
...
...
++endif
The above technique works regardless of whether you use ++include or ++include'once, and is independent of the context (global/main or within a function). Which is exactly what you want for structure definitions, since they can only be defined once for the entire program.
The only downside of the above technique is that it still appears as if the compiler is including the file (assuming you pay any attention to the compiler messages). To make it absolutely clear that the file is really only being processed once, you could add a
++message , e.g.
++ifndef INC_XTREE_SDF
define INC_XTREE_SDF = 1
...
++else
++message Skipping XTREE.SDF (already processed)
++endif
(I'm assuming here that your ++ifndef conditional block encloses the entire include file, but of course you could use the technique to carve out sections within an include file.)
3. Use
++ifnmap instead of
++ifndef . This technique applies to include files containing map statements, where you might just want to retain the ability to include them multiple times (within individual functions), e.g.
!myrecord.map
++ifnmap my'record
map1 my'record
map2 my'id,s,6
...
++endif
If you try to ++include myrecord.map more than once at the global level of your program, it will be skipped (otherwise you'd get a "variable already defined" error). But if you include it once at the global level and again within a function, or within multiple functions, then the include will proceed, because even though the map statements are identical, they generate unique variables in the separate contexts, and thus the ++ifnmap my'record fails.
(For unknown reasons, it seems that ++ifnmap never got documented. Ty?)
4. Tracking down compiler errors within nested includes - although the compiler outputs an extra "+" preceding the error line number for each level of nested include, it is still sometimes hard to figure out which include file the error is coming from. If you find yourself spending more than a couple of seconds trying to figure that out, the quick solution is to re-compile with the
/B switch for
background mode, creating a comma-delimited .LST file containing the actual file and line number of each error. (Or, better yet, compile with The Editor, which uses that technique to allow you to click on each error and go right to the source file and line.)
5. Determining which version of a ++include is within a RUN or SBX module. You may well ignore this issue by just recompiling your entire app every time you change any include file. Still, sometimes you may find yourself looking at a particular RUN file, maybe at an end-user site, and wondering whether a particular include file fix was incorporated or not. One approach to this issue is to copy the scheme used by A-Shell and VERSYS.LIT. You may notice that many of the A-Shell common include modules contain a map statement similar to the following:
map1 VERSYS_XTREE_SDF,s,40,">>@VERSYS(1)->>xtree.sdf[135]"
The idea here is to include a string containing a version/id and with a sufficiently unique prefix that we can locate it amongst the binary chaos of a RUN module, which is exactly what the VERSYS utility does, e.g:
.versys copy.lit
COPY.LIT -- A-Shell Version & System Information:
File Version: 3.3(137)
Program format: 0xF166 (compil/x?; NOT AMOS compatible)
System Resource: ashell.bsi[116]
System Resource: ashello.def[114]
System Resource: ashell.def[222]
System Resource: cmdlin[41]: max devs = 300, unlimited ppns
Here we can see that COPY.LIT was compiled with version [41] of the CMDLIN.BSI include file, which supports unlimited ppns. This is because CMDLIN.BSI contains:
map1 VERSYS'CMDLIN,s,80,">>@VERSYS(1)->>cmdlin[41]: max devs = 300, unlimited ppns"
The above two examples should be sufficient to show the pattern. Basically, VERSYS searches for the string ">>@VERSYS(1)->>" and then displays everything after that.
You can use VERSYS yourself as is, or, if you'd like to use the same concept but customize it for your own purposes, you can find the source and all of its include modules
here .