Path: utzoo!attcan!uunet!tut.cis.ohio-state.edu!pt.cs.cmu.edu!andrew.cmu.edu!ghoti+ From: ghoti+@andrew.cmu.edu (Adam Stoller) Newsgroups: comp.lang.c Subject: Prototyping in an imperfect world.... Message-ID: Date: 29 Jun 90 22:58:59 GMT Organization: Information Technology Center, Carnegie Mellon, Pittsburgh, PA Lines: 164 (lengthy - but lot's of white-space -- sorry) Some of this is old news, but some may not be - and I'm a bit interested in some [reasonable] opinions on either.... [prolog (harmonicas playing in the background?)] As many of us are aware - there is a wonderful thing called prototyping -- especially with respect to ANSI-C. Unfortunately we are also aware that while some people on some platforms may have full prototyping abilities others may not -- and this tends to cause problems for people who'd like to try and make their code portable.... There are two different places where this comes into effect: 1) extern reference declarations 2) function definitions The first I believe has been reasonably well addressed. For those not familliar with a way to easily handle it I provide the following [possible] definition: /* external reference declarations */ /* use: extern int foo P_((int x, char c, long d)); */ #ifdef __STDC__ #define P_(args) args #else #define P_(args) () #endif A hack? Yes, but a farily clean and reasonable one (IMO). The second situation however I haven't seen addressed much. About the only way I've seen people deal with it was by doing something like: int #ifdef __STDC__ foo(int x, char c, long d) #else foo(x, c, d) int x; char c; long d; #endif { .... } This, IMO, is both a painful (to implement/maintain) and ugly hack - and one which I'd like to avoid if possible. To this end - one of the people I work with came up with the idea for a macro to use for the definitions - which I'd like to share with you now and get some opinions on: #ifdef __STDC__ #define PP_(a) ( #define V_ void) #define S_ , #define E_ ) #else #define PP_(a) a #define V_ #define S_ ; #define E_ ; #endif This allows the above example to be defined like: int foo PP_((x, c, d)) int x S_ char c S_ long d E_ { ...... } Or int foo PP_(()) V_ { .... } Which for the most part is reasonably close to the old-style definitions though nevertheless hackish. [Question Time] Q-1) Is it too ugly for words ? Personally, I admit not liking the loss of the commas/semicolons which are pretty much second-nature when typing in the code, but other than that it doesn't bother me too much. Q-2) Is it too hackish to be considered reasonable ? I think it stays within the boundary of reasonable hacks (whatever that boundary may be, unless you're a purist who considers any hack evil??) Q-3) Is it reasonably maintainable ? It suffers a little of the same problem as the earlier #ifdef example - in that there are still two places where the name and number of arguments must be maintained - but I think this one wears a little better in that it's really only as bad(?) as the old-style definitions. Q-4) Can it be made better ? Mostly I mean this in terms of the symbols chosen for the macros, and possibly the macros themselves. Certainly there are tons of alternatives ("use A_ instead of S_", etc) - but I'm looking for something a bit more substantial, with reasonable explanation. (hey, it's always possible that we might have gotten it "right" the first time....:-) (FYI: V_ == void, S_ == separator, E_ == end-of-list) [Visualization] (some blank lines removed from preprocessor output) [ | <-column 0] ---------------- > cat foo.c /* #define's as shown above */ int fname PP_((a1, a2)) int a1 S_ int a2 E_ { } struct lconv *ansi_localeconv PP_(()) V_ { } ---------------- > cc -E -D__STDC__ foo.c int fname ( int a1 , int a2 ) { } struct lconv *ansi_localeconv ( void) { } ---------------- > cc -E -U__STDC__ foo.c int fname (a1, a2) int a1 ; int a2 ; { } struct lconv *ansi_localeconv () { } ----------------