Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: Writing readable code Message-ID: <7220@mimsy.UUCP> Date: Fri, 26-Jun-87 17:49:53 EDT Article-I.D.: mimsy.7220 Posted: Fri Jun 26 17:49:53 1987 Date-Received: Sat, 27-Jun-87 11:45:23 EDT References: <8286@ut-sally.UUCP> <7001@alice.UUCP> <364@sol.ARPA> <1158@copper.TEK.COM> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 105 In article <1158@copper.TEK.COM> stevesu@copper.TEK.COM (Steve Summit) writes: >It's unfortunate that people are finding reasons to deprecate >typedefs, which can be used to substantially *increase* the >readability and portability of code. The two complaints above >follow from the fact that typedefs, like many features of C, can >be (ab)used, with devastating effect, to make code absolutely >impenetrable. This connects with my own distrust of adding strong typing to C. Here is a real live example. Looking in , we find struct _iobuf { ... }; ... #define FILE struct iobuf (Here the define is much like a typedef; either would do.) This virtually (but not in fact) adds a type to the set of types the programmer must understand. `FILE' is such a well-known type that no one has any more trouble with it than with `int'. For reasons that I shall not explain, I have decided to change the internals of stdio such that user code can `open' a set of I/O functions. Stdio will call these functions instead of `read' and `write', `seek', and `close'. For instance: struct info info; /* where to write, etc. */ void *p = &info; /* p will be given to writefn */ int writefn(); /* and writefn will write things */ FILE *fp = write_function_open(p, writefn); ... fprintf(fp, fmt, ...) ... would open `writefn' for writing, and would call `writefn(p, buf, n)' instead of `write(fileno(fp), buf, n)'. This can be used for a dynamically allocating `sprintf', or for a curses-style `wprintf', or whatever. But this is not properly typed: writefn() is not a function of no arguments. This: int writefn(void *p, const char *buf, int n); is the proper declaration. But stdio can do more than just write: it can read, and it can seek; and it eventually needs to close the `file'. So the general form is this: FILE *funopen(); Whoops, we forgot the types of the arguments. FILE *funopen(void *p, int (*readfn)(), int (*writefn)(), long (*seekfn)(), int (*closefn)()); All these parentheses are annoying, so let us add a few typedefs: typedef int (*iofun)(); typedef long (*seekfun)(); FILE *funopen(void *p, iofun readfn, iofun writefn, seekfun seekfn, iofun closefn); But wait, this is not right! The arguments to close are not the same as those to read! We need to declare *everything*, to get those types right. typedef int (*iorfp)(void *p, char *buf, int n); typedef int (*iowfp)(void *p, const char *buf, int n); typedef long (*ioseekfp)(void *p, long off, int whence); typedef int (*ioclosefp)(void *p); FILE *funopen(void *p, iorfp readfn, iowfp writefn, ioseekfp seekfn, ioclosefp closefn); Well, we finally did it. But look at the cost: Four typedefs just for the arguments to `funopen'. It really takes four; all four are different. We could get away with three, by lying and pretending that the write function is allowed to clobber the contents of *buf: typedef int (*iorwfp)(void *p, char *buf, int n); but if we are going to use strong typing at all, we should keep the two distinct. Well, we could discard the typedefs entirely, since they are just aliases and the declaration of an ior function will have to match the use of the iorfp: FILE *funopen( void *p, int (*readfn)(void *p, char *buf, int n), int (*writefn)(void *p, const char *buf, int n), long (*seekfn)(void *p, long off, int whence), int (*closefn)(void *p)); It is hard to say which is worse. This does not steal away any of the global namespace, as do the typedefs, but it is horribly monolithic. This demonstrates what happens with strongly typed systems: They lead to a profusion of types, and it becomes difficult to keep them straight. Does database_write call an iowfp function, or did we put a database type layer over it, and it calls a dbiowfp? Of course, these types are indeed different, and calling an iowfp function when you were supposed to call a dbiowfp is likely to be a drastic error. So all this strong typing may be good. But it certainly does not look like C anymore. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) Domain: chris@mimsy.umd.edu Path: seismo!mimsy!chris