friedl@mtndew.Tustin.CA.US (Steve Friedl) (08/12/90)
Hi folks, I like to use function prototypes when possible, so in my header files I do something like this: #ifdef USE_PROTO # define PROTO(name, args) name args #else # define PROTO(name, args) name ( ) #endif extern PROTO(int printf, ( const char *, ... ) ); extern PROTO(char *strcpy, ( char *, const char * ) ); and so on (note: I know that printf and strcpy are in other headers, I'm just using familiar examples). I have been doing this for about two years and have been really happy with it. However, I have seen it done "the hard way": #ifdef USE_PROTO extern int printf(const char *, ... ); extern char *strcpy(char *, const char * ); #else extern int printf() extern char *strcpy(); #endif This looks like a real maintenance nightmare, but some of the people who do it are people I respect, so I gotta wonder if they know something that I don't know on this one. Are there any gotchas on doing it with the flavor of the way I've done it? Note: I do know that things like signal() won't fit into my mold, but these are so much in the minority that I don't mind doing those few "the hard way". Steve -- Stephen J. Friedl, KA8CMY / Software Consultant / Tustin, CA / 3B2-kind-of-guy +1 714 544 6561 / friedl@mtndew.Tustin.CA.US / {uunet,attmail}!mtndew!friedl If Larry Ellison says it, it must be true.
daveb@llama.Ingres.COM (here kitty, kitty...) (08/14/90)
It's a mess. The PROTO style of macro is convenient to write, but violates one of the secret laws of C style: "Thou shalt not change the syntax of the language with macros." Using them messes up all sorts of syntax-sensitive tools, and is confusing to the unitiated. The solution of #ifdef around all the specific cases is bugly and a pain to use, but it is correct and portable when done correctly. Numberous people believe tha answer is really to write a filter that turns ANSI code into Classic-C code, inserting all the casts into function calls as needed. This would be neet, but nobody knows of the existance of such a program. *sigh* The person who writes one and posts it will be *very* popular! -dB In <493@mtndew.Tustin.CA.US> friedl@mtndew.Tustin.CA.US (Steve Friedl) writes: >Hi folks, > > I like to use function prototypes when possible, so in my >header files I do something like this: > > #ifdef USE_PROTO > # define PROTO(name, args) name args > #else > # define PROTO(name, args) name ( ) > #endif > > extern PROTO(int printf, ( const char *, ... ) ); > extern PROTO(char *strcpy, ( char *, const char * ) ); > >and so on (note: I know that printf and strcpy are in other >headers, I'm just using familiar examples). I have been doing >this for about two years and have been really happy with it. >However, I have seen it done "the hard way": > > #ifdef USE_PROTO > extern int printf(const char *, ... ); > extern char *strcpy(char *, const char * ); > #else > extern int printf() > extern char *strcpy(); > #endif > >This looks like a real maintenance nightmare, but some of the >people who do it are people I respect, so I gotta wonder if they >know something that I don't know on this one. Are there any >gotchas on doing it with the flavor of the way I've done it? > >Note: I do know that things like signal() won't fit into my mold, >but these are so much in the minority that I don't mind doing those >few "the hard way". > > Steve > >-- >Stephen J. Friedl, KA8CMY / Software Consultant / Tustin, CA / 3B2-kind-of-guy >+1 714 544 6561 / friedl@mtndew.Tustin.CA.US / {uunet,attmail}!mtndew!friedl > >If Larry Ellison says it, it must be true. Ahem. "Bottom of the 4th, Cooper pitching" - tibetan baseball David Brower: daveb@rtech^H^H^H^H^Hingres.comd
karl@haddock.ima.isc.com (Karl Heuer) (08/16/90)
In article <1990Aug13.172452.14217@ingres.Ingres.COM> daveb@llama.Ingres.COM (here kitty, kitty...) writes: >In <493@mtndew.Tustin.CA.US> friedl@mtndew.Tustin.CA.US (Steve Friedl) writes: >>[I like to use:] >> #ifdef USE_PROTO >> # define PROTO(name, args) name args >> #else >> # define PROTO(name, args) name ( ) >> #endif >> extern PROTO(char *strcpy, ( char *, const char * ) ); >>However, I have seen it done "the hard way": >> #ifdef USE_PROTO >> extern char *strcpy(char *, const char * ); >> #else >> extern char *strcpy(); >> #endif >>This looks like a real maintenance nightmare... Interesting; I would have said that the *former* method looks nightmarish. >Numberous people believe tha answer is really to write a filter that >turns ANSI code into Classic-C code, That's what I recommend, and what I use for myself%. >inserting all the casts into function calls as needed. [But no such program >exists.] Wait a minute; now you've changed the problem. The original two proposals didn't handle the situation of mismatched argument types in a Classic C environment, so it's unfair to make that a requirement. The tool I use isn't an ANSI-to-Classic converter, but it is a sufficiently powerful deprotoizer for my purposes. Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint ________ % In .c files and in single-project .h files, anyway. Global header files tend to be static, so I just write them once, using what Steve calls the "hard way", and then ignore them once they're installed.
rtm@christmas.UUCP (Richard Minner) (08/17/90)
In article <493@mtndew.Tustin.CA.US>, Steve Friedl writes: > > I like to use function prototypes when possible, so... > #ifdef USE_PROTO > # define PROTO(name, args) name args > #else > # define PROTO(name, args) name ( ) > #endif >... > However, I have seen it done "the hard way": > #ifdef USE_PROTO > extern int printf(const char *, ... ); >... > This looks like a real maintenance nightmare, but some of the > people who do it are people I respect, so I gotta wonder if they > know something that I don't know on this one. Are there any > gotchas on doing it with the flavor of the way I've done it? First, one caution that some may still not be aware of (even though it's covered in the FAQ, question 22 :-), which is, briefly, that with Classic style function declarations and definitions, args of type char, short and float are (silently) promoted to int and double, whereas with ANSI style they are not, e.g: extern int foo(char c); ... int foo(c) char c; doesn't work. Those who don't understand should read FAQ, Q #22 (or thereabouts). So you can't use your PROTO() macro with functions that take char's, short's or float's as arguments. Other than that, it *works* fine (see end of article). In fact, the "updated version of the `Indian Hill C Style and Coding Standards'" (which was posted here a while back, and by the way, thanks!), includes a `PROTO()' macro similar to yours, like this: #ifdef HAVE_ANSI_PROTOTYPES # define PROTO(args) args #else # define PROTO(args) () #endif I find this much cleaner, because the results look (to me) more like normal declarations, and because it supports things like signal() (and I use a fair number of `functions taking functions as arguments'). It takes advantage of the fact that the only difference between ANSI prototypes and Classic decls is the presence of the argument list. I actually use `P_()', because it's much more cryptic and hard to read ;-), so signal() would look like: extern void ( *signal P_((int sig, void (*func)(int))) ) P_((int)); which is admittedly a bit ugly with all the ()'s, but: #ifdef HAVE_ANSI_PROTOTYPES extern void (*signal (int sig, void (*func)(int))) (int); #else extern void (*signal())(); #endif isn't much of an improvement. By the way, don't forget typedefs, e.g: typedef int (*func_type) P((int, double, const char *)); As to why some experienced programmers would use the `#if' approach instead, it's probably because they don't like macros that alter the syntax of C, which is *not* an unreasonable position, mind you. It's your code, you choose. Just don't be surprised if someone someday is irritated by it. I can't imagine any other reasons to prefer `#if'. -- Richard Minner || {uunet,sun,well}!island!rtm (916) 736-1323 || || Island Graphics Corporation Sacramento, CA ||
daveb@ingres.com (When a problem comes along . . . you must whip it) (08/17/90)
Please pardon the lengthy inclusion. In <17424@haddock.ima.isc.com> karl@kelp.ima.isc.com (Karl Heuer) writes: >In article <1990Aug13.172452.14217@ingres.Ingres.COM> I write: >>In <493@mtndew.Tustin.CA.US> friedl@mtndew.Tustin.CA.US (Steve Friedl) writes: >>>[I like to use:] >>> #ifdef USE_PROTO >>> # define PROTO(name, args) name args >>> #else >>> # define PROTO(name, args) name ( ) >>> #endif >>> extern PROTO(char *strcpy, ( char *, const char * ) ); >>>However, I have seen it done "the hard way": >>> #ifdef USE_PROTO >>> extern char *strcpy(char *, const char * ); >>> #else >>> extern char *strcpy(); >>> #endif >>>This looks like a real maintenance nightmare... > >Interesting; I would have said that the *former* method looks nightmarish. This is my conclusion as well; It defeats tags generators and any other syntax dependant tool. >>Numberous people believe tha answer is really to write a filter that >>turns ANSI code into Classic-C code, > >That's what I recommend, and what I use for myself%. > >>inserting all the casts into function calls as needed. [But no such program >>exists.] > >Wait a minute; now you've changed the problem. The original two proposals >didn't handle the situation of mismatched argument types in a Classic C >environment, so it's unfair to make that a requirement. The tool I use isn't >an ANSI-to-Classic converter, but it is a sufficiently powerful deprotoizer >for my purposes. Perhaps; I'm uneasy. One statement of the problem is "how do I reasonably write code for both old and new C without going nuts?" I think most people would prefer NOT to have to run lint over a program to verify arg matches when they've gone to the trouble of adding prototypes -- prototypes solve the problem completely. But if your method does not ensure that casts are inserted in function calls, you've made no advance on ensuring the type-correctness of the old-C. If you go for the "write ansi and deprotoize" solution, you may have gone backwards, because you may not even think to put casts in arg lists. So, unless the deprotoize tool does put in the casts, you need to run lint. Once you're running lint, you've lost some of the value-added by ANSI. I'm still inclined to think you need to have casts inserted, and that nothing short of that is reliable enough to trust. My apologies to Karl for not having dug into my archives earlier and finding his converter. It does indeed do what he advertises, I'm just not convinced it is enough. Also see "comp.sources.misc v06i068: Lex programme to convert pANS to Classic C" by Robert Lupton (lupton@uhccux.uhcc.Hawaii.Edu), which does the same sort of thing. Karl's is ~172 lines, the other ~246. Someone suggested by mail that the "unprotoize" hack to GCC does the job I want. (If it doesn't, it's a lot of baggage to be carrying around compared to Karl's and Lupton's!) Does anyone know where I can get a copy to try? I have gcc-1.37.1 handy. thanks, -dB -- "In my opinion, most C programs should be indented 6 feet under." David Brower: {amdahl, cpsc6a, mtxinu, sun}!rtech!daveb daveb@ingres.com
andrew@alice.UUCP (Andrew Hume) (08/19/90)
if one adopts the attitude that prototypes are good and old C is just for compatibility, one is drawn to the technique of using prototypes only in the code and running an awk script to produce old style declarations. this is simple to do if one adopts conventions such as all prototypes on one line etc.