evil@arcturus.uucp (Wade Guthrie) (10/31/90)
I would like to ask what, if any, style standard exists for the
distribution of prototypes within header files for a large project.
Should my project have a single "proto.h" which comprises all of the
global (i.e., non-static) function prototypes or should separate
headers exist?
Thanks.
--
Wade Guthrie (evil@arcturus.UUCP) | "He gasped in terror at what sounded
Rockwell International; Anaheim, CA | like a man trying to gargle while
My opinions, not my employer's. | fighting off a pack of wolves"
| Hitchhiker's Guide
burley@world.std.com (James C Burley) (11/03/90)
In article <1990Oct30.182347.28809@arcturus.uucp> evil@arcturus.uucp (Wade Guthrie) writes: I would like to ask what, if any, style standard exists for the distribution of prototypes within header files for a large project. Should my project have a single "proto.h" which comprises all of the global (i.e., non-static) function prototypes or should separate headers exist? Thanks. -- Wade Guthrie (evil@arcturus.UUCP) | "He gasped in terror at what sounded Rockwell International; Anaheim, CA | like a man trying to gargle while My opinions, not my employer's. | fighting off a pack of wolves" | Hitchhiker's Guide I think most everyone has their own style, and most groups, and perhaps even some companies. But I'd recommend against using a single project-wide prototype file. Think of organizing your source files around the objects they deal with: for example, put all the code that "knows" about lexing into a lex file, all the code that "knows" about expression parsing into an expr.c file, and so on. Then, each source (.c) file contains functions and other things (global and internal static data, internal static functions) that handle the internals of the object (or sometimes objects, if they're intricately interwoven) and present a uniform, abstracted interface to the outside world. By uniform, I mean you present functions that make sense to someone who knows what the source file does, but not how it does it -- in particular, I'd suggest that outside users never directly access global data defined within a source file. Instead, make that access path via a macro that could double as a function so changing how the module is implemented won't require changes to code that uses it. (This last statement is the ideal; not possible to realize fully since often when designing it is not always clear just what the ideal interface is and sometimes the ideal interface would just be too slow. But in each individual case, this ideal can often be reached 100%, and for the cases that can't, use some consistent style like a strange naming convention that clearly identifies in a mnemonic fashion, along with the more verbose comments, that "special insight into a module's internal operation" happens at various places outside the module.) Now, for each such source file, write a .h file that completely describes the interface to that source file. Given the name of the file, xyz, enclose all its actual "code" (declarations) with: #ifndef XYZ_H #define XYZ_H ...declarations... #endif These declarations provide the function prototypes you ask about along with enums (or #defines) for all the constants defined by the module for outside use. They also provide "extern" declarations for global data accessed by outside users via macros #define'd within the declarations. I use a naming convention of adding a trailing underscore to anything I define in a module's .c or .h file that outside users aren't supposed to touch via their source code but which they will access anyway via macros defined in the same .h file, as in: xyz.h: ... extern int xyz_count_; ... #define xyz_count() xyz_count_ /* Return current count. */ ... xyz.c: ... int xyz_count_ = 0; /* The actual storage for count. */ ... This way, a simple rule can be applied: a given module should not reference any name ending in underscore (or substitute your own favorite naming convention) unless the name it references is defined by itself (and not some other module). In any project larger than "quicky" I always stick the module name in front of every name the module exports (global) anyway, and even go as far as doing that for internal static names in the .c file (with "_" suffixing the name). Lots of people don't like this approach, but it works for me and I never get naming conflicts. Names get long but I type fast. Object orientation (C++) helps reduce the need to do this sort of thing, I suspect. Now any .c file whose SOURCE code (not the cpp-processed level, i.e. once #defines are resolved; that's "someone else's problem") references anything declared by an outside module is supposed to "#include" the .h file for that module. Further, any .h file whose source code should do the same thing. If you stick to certain orderings in the .h file, like doing the "easy" things like enums, non-macro #defines (though macro #defines can be done here), and "typedef" statements before the #includes, then doing the "struct" definitions referenced by "typedefs", the function prototypes, the "extern" definitions, and so on, you can even end up with a project where modules interreference each other (as in "foo.h" defines structures incorporating types defined by "bar.h" and vice versa) without having any ordering dependencies for the #includes in any file. Generally, I have every .c file include a project-wide file named project.h which defines some truly common things I use like bool, TRUE, FALSE, ARRAY_SIZE, CAT, etc, and each .c file also includes whatever standard headers it needs. Similarly, a .h file that itself depends on a standard header includes it (though I don't think I've ever had such a dependency, but an example might be, in xyz.h, "#define xyz_is_alpha(c) isalpha(c)", which means xyz.h should #include <ctype.h>). But .h files should not include project.h, I think of that as a special case project-wide file. I have a boilerplate for my .c and .h files that try to allow one to define things in the appropriate orders. Right now I know of a bug in my .c boilerplate: it places the definitions of internal static storage (as in an array used only within the .c code) before the function prototypes section. This works unless you want an internal array containing pointers to internal static functions, as I just discovered two days ago after using this basic boilerplace for over a year! So I'm going to move my function prototypes above my internal static storage section, since the only things function prototypes can depend on are types (and structs and such), but not internal storage definitions, whereas internal storage definitions can depend on function prototypes being present. All this might seem like trouble, but once you have boilerplates that are carefully thought out (or, like mine, had a chance to grow quickly in a complicated application and are relatively stable), creating a new module and connecting it up is a matter of filling in the blanks. Further, I just basically never have to waste time tracking down #include dependencies (which on previous projects involving multiple programmers I've spent hours on once in a while) or other egregious coding errors. Naming conventions can be a pain but if you think of a name as not only being a mnemonic for a description but also for the location (the .h/.c file pair) and accessibility (public or private) of a function, it'll seem less painful, and you'll begin to reap the rewards (fewer silly bugs and quicker location of function decls and defs) after sticking with these techniques for a while. They aren't new or anything -- data abstraction, data hiding (as much as is reasonable in C), encapsulation, and so on -- but sometimes people think they can't be done in C or done with optimal results, and I think they can and believe I've proved it at least for myself. (I've written a 50,000 line program over the last year -- a Fortran Front End for a forthcoming GNU Fortran -- using these techniques throughout, and they've been great.) Do use a compiler with "require prototypes" or some such thing turned on. Or use lint (which I don't have and have never yet used). Don't waste another moment of time run-time-debugging a parameter passing problem that could have been detected earlier via prototypes! Hope this rambling helps. Let me know if you want me to email my .c and .h boilerplates. James Craig Burley, Software Craftsperson burley@world.std.com
daves@hpopd.HP.COM (Dave Straker) (11/03/90)
>I would like to ask what, if any, style standard exists for the >distribution of prototypes within header files for a large project. >Should my project have a single "proto.h" which comprises all of the >global (i.e., non-static) function prototypes or should separate >headers exist? >---------- I'd split it by functional area. Thus for the widget handling subsystem, use widget.h to contain all interface items to that subsystem. Then anyone calling to widget handling includes widget.h and calls away, references widget global data (!) etc. Within widget.h, organise data by sub-functional area, rather than all prototypes, all macros, etc. This will ease reading and splitting the file (if it comes to that). Dave Straker Hewlett Packard - PWD divn