andrew@resam.dk (Leif Andrew Rump) (08/08/90)
Well I know it doesn't look smashing but instead of writing: #define FILE_FOOTER_ERROR 1 #define DRAW_FOOTER_ERROR 2 #define GRAP_FOOTER_ERROR 3 #define BASE_FRAME_CREATE_ERROR 4 char errortext[][40] = { /*00*/ "234567890123456789012345678901234567890", /*01*/ "File footer error", /*02*/ "Draw footer error", /*03*/ "Grap footer error", /*04*/ "Base_frame creation error", } which I may write wrong because I forget a line or add a line. This is allowed by my compiler (I figure the preprocessor removes it and performs symbol substitution before giving it to the compiler) and I know that all the lines are there! char errortext[][40] = { "234567890123456789012345678901234567890", #define FILE_FOOTER_ERROR 1 "File footer error", #define DRAW_FOOTER_ERROR 2 "Draw footer error", #define GRAP_FOOTER_ERROR 3 "Grap footer error", #define BASE_FRAME_CREATE_ERROR 4 "Base_frame creation error", } I am a pascal-programmer (aha, that is why :-) ) in my real life and I could also use union/enumerated types. Leif Andrew Leif Andrew Rump, AmbraSoft A/S, Stroedamvej 50, DK-2100 Copenhagen OE, Denmark UUCP: andrew@ambra.dk, phone: +45 39 27 11 77 / Currently at Scandinavian Airline Systems =======/ UUCP: andrew@resam.dk, phone: +45 32 32 22 79 \ SAS, RESAM Project Office, CPHML-V, P.O.BOX 150, DK-2770 Kastrup, Denmark > > Read oe as: o <backspace> / (slash) and OE as O <backspace> / (slash) < <
karl@haddock.ima.isc.com (Karl Heuer) (08/09/90)
In article <1990Aug8.100614.1223@resam.dk> andrew@resam.dk (Leif Andrew Rump) writes: >[To avoid having #define indices get out of sync with constant arrays,] > char errortext[][40] = { > "234567890123456789012345678901234567890", > #define FILE_FOOTER_ERROR 1 > "File footer error", > #define DRAW_FOOTER_ERROR 2 > "Draw footer error", > } Yeah, it works; I don't recommend it. It doesn't work as well if you have *two* arrays that use the same enumerated indices, for example; or if you want to put the indices and the strings in separate files. I would rather keep the definitions separate, and add a comment like `/* must be kept in sync! */'. I recently proposed an extension to allow labeled initializers: #define FILE_FOOTER_ERROR 1 #define DRAW_FOOTER_ERROR 2 char errortext[3][40] = { 0: "234567890123456789012345678901234567890", DRAW_FOOTER_ERROR: "Draw footer error", FILE_FOOTER_ERROR: "File footer error", }; This might make it into gcc (and maybe from there into C-2001?). >I am a pascal-programmer I might have guessed. A C programmer would likely have used `char *[]' (instead of padding all the strings to the same length like you have to do in non-extended Pascal) and made the indices start at zero. Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
scs@adam.mit.edu (Steve Summit) (08/09/90)
In article <1990Aug8.100614.1223@resam.dk> andrew@resam.dk (Leif Andrew Rump) worries about keeping error numbers in sync with an array of error message strings so that the array can be indexed by the array number. Whenever I'm serious about making the connection between error numbers and messages explicit rather than implicit, I do so quite, er, explicitly: #define FILE_FOOTER_ERROR 1 #define DRAW_FOOTER_ERROR 2 #define GRAP_FOOTER_ERROR 3 #define BASE_FRAME_CREATE_ERROR 4 struct errmess { int em_number; char *em_text; } errmesses[] = { FILE_FOOTER_ERROR, "File footer error", DRAW_FOOTER_ERROR, "Draw footer error", GRAP_FOOTER_ERROR, "Grap footer error", BASE_FRAME_CREATE_ERROR,"Base_frame creation error", }; This essentially just removes the comment delimiters placed around the implicit subscripts that are usually provided to remind the programmer what is going on (i.e. /*01*/ "File footer error", in Leif's example). This setup still has the disadvantages that the two components of each line of the structure array definition are quite repetetitive (FILE_FOOTER_ERROR vs. "file footer error"), and every new error number must be added in two places (another #define and another row in the table). A special-purpose preprocessor (perhaps a sed or awk script) could easily automate the table building, if appropriate. Of course, once the array of structures is set up, it's a simple matter to write a little routine char *errtext(int errnum) which takes an error number and searches the array, returning (similarly to the standard library routine strerror) the corresponding error string, or "Error %d", (with %d filled in, of course) for the eventual undefined or illegal error. The scheme has a few other advantages: error numbers can start at 0, 1, or any other number, they can be sparse or negative, and the existence (and mandated use; nobody can cheat and index on an array) of the errtext() routine means there's a convenient hook (namely, the errtext routine itself) for changing things later to pull error messages out of a file at run time or some other useful game. Someone will howl that a linear scan of the error number/string pairing array would be "too inefficient," but I would point out that (a) it wouldn't, if there are a reasonable number of error numbers, and (b) error message printing is not usually a bottleneck, and (c) you could rewrite the routine to use binary search (or, God help you, a hash table) if you really wanted to. (That's another nice thing about a functional interface: you can always drop in some other implementation, that needs to gain control to do something, that you wouldn't be able to do if the "interface" were simply the access by the "caller" of a global variable or array.) Steve Summit scs@adam.mit.edu
diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/09/90)
In article <1990Aug8.100614.1223@resam.dk> andrew@resam.dk (Leif Andrew Rump) writes: >char errortext[][40] = { > "234567890123456789012345678901234567890", >#define FILE_FOOTER_ERROR 1 > "File footer error", >#define DRAW_FOOTER_ERROR 2 > "Draw footer error", >#define GRAP_FOOTER_ERROR 3 > "Grap footer error", >#define BASE_FRAME_CREATE_ERROR 4 > "Base_frame creation error", > } Yes, it is perfectly legal, but as you suggested (and I deleted) it might not be considered aesthetic. >I am a pascal-programmer (aha, that is why :-) ) in my >real life and I could also use union/enumerated types. If you want enumeration types, C almost learned them about 11 years ago. Under ANSI, they've been almost learned in a standardized way. However, you might have problems with pre-ANSI compilers (where a single compiler would often be inconsistent with itself). typedef enum error_t { Filler_to_match_Digit_String, /* I would delete this and the string */ FILE_FOOTER_ERROR, DRAW_FOOTER_ERROR, GRAP_FOOTER_ERROR, BASE_FRAME_CREATE_ERROR, Number_of_Error_Codes /* Put new insertions BEFORE this! */ } error_t; /* If you use Posix, then you can't end it with _t */ char errortext[][40] = { "234567890123456789012345678901234567890", /* I'd make it a comment */ "File footer error", "Draw footer error", "Grap footer error", "Base_frame creation error", } int junk = 1 / (sizeof errortext[0] * Number_of_Error_Codes == sizeof errortext); /* A compile-time error here means mismatched quantities */ -- Norman Diamond, Nihon DEC diamond@tkou02.enet.dec.com This is me speaking. If you want to hear the company speak, you need DECtalk.
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (08/10/90)
In article <1910@tkou02.enet.dec.com>, diamond@tkou02.enet.dec.com (diamond@tkovoa) writes: > In article <1990Aug8.100614.1223@resam.dk> andrew@resam.dk (Leif Andrew Rump) writes: > >char errortext[][40] = { > > "234567890123456789012345678901234567890", > >#define FILE_FOOTER_ERROR 1 > > "File footer error", Instead of all this, suppose you have k separate arrays, k >= 1, that you want to initialise "in parallel". You want to give names to the numbers, and a name to the total number. The obvious method is to construct a miniature language a la Bentley. Take a data file like this: #File: mktable.demo # EntryCt; int whence; int whither; char *name Butcher; 1; 2; "Butcher" Baker; 1; 3; "Baker" Bellman; 2; 3; "Bellman" Boojum; 0; 0; NULL #End: mktable.demo Here the first non-comment line has the form <total>; <type 1> <name 1>; ... <type k> <name k> where <total> is the name that will be #defined to the number of elements in each array, <name i> is the name of the ith array to be initialised, and <type i> is the type of its elements. Subsequent non-comment lines have the form <record name>; <value 1>; ... <value k> where <record name> is the name that will be #defined to the record number, and <value i> is a C expression which is to be one of the initial values of array <name i>. We feed this through the following AWK script: #File: mktable.awk # BEGIN { # note that no header nor data seen yet k = 0 # number of fields required in each record r = 0 # number of data records processed so far p = 0 # number of data *values* saved in data[] } $0 ~ /^[ \t]*#/ { # ignore comment lines next } k == 0 { # first non-comment line is header if (NF < 2) { print "Error in line " NR ": 2 or more names expected" exit } k = NF # number of fields to be in EACH line for (i = 1; i <= NF; i++) name[i] = $i next # don't treat this as data! } k != 0 { # this must be a data record if (NF != k) { print "Error in line " NR ": wrong number of fields" exit } print "#define", $1, r # define the record number r++ # then save the data away for (i = 2; i <= NF; i++) data[p++] = $i } END { # all records seen if (k == 0) { # never saw a header print "Error; no header record present" exit } if (r == 0) { # never saw any data print "Error; no data records present" exit } # otherwise, data[] is r blocks of (k-1) strings print "#define", name[1], r for (i = 2; i <= k; i++) { print name[i] "[] = {" for (t = i-2; t < p; t += k-1) print "\t" data[t] "," print "};" } } #End: mktable.awk In UNIX it is easy to bolt all this together awk -F\; -f mktable.awk mktable.demo | cb >tables.c ^field separator ^awk program ^data file ^result (Note that using ; as the field separator allows spaces in expressions. If you want "strings" or 'ints' containing ";", use some other character.) The result is #define Butcher 0 #define Baker 1 #define Bellman 2 #define Boojum 3 #define EntryCt 4 int whence[] = { 1, 1, 2, 0, }; int whither[] = { 2, 3, 3, 0, }; char *name[] = { "Butcher", "Baker", "Bellman", NULL, }; In MS-DOS, it's a little harder (you might have to get the MKS awk, for example). On the other hand, it would be pretty easy to translate this awk program to C. In any case, just #include the result into your C program, and Bob's your uncle! -- Taphonomy begins at death.
ronald@robobar.co.uk (Ronald S H Khoo) (08/11/90)
In article <3541@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: > In MS-DOS, it's a little harder (you might have to get the MKS awk, > for example). GAWK was posted to comp.binaries.ibm.pc. No problem. -- Eunet: Ronald.Khoo@robobar.Co.Uk Phone: +44 81 991 1142 Fax: +44 81 998 8343 Paper: Robobar Ltd. 22 Wadsworth Road, Perivale, Middx., UB6 7JD ENGLAND.
friedl@mtndew.Tustin.CA.US (Steve Friedl) (08/20/90)
Steve Summit writes: > [Others] worry about keeping error numbers in sync with an array of > error message strings so that the array can be indexed by the > array number. > > Whenever I'm serious about making the connection between error > numbers and messages explicit rather than implicit, I do so > quite, er, explicitly: > > #define FILE_FOOTER_ERROR 1 > #define DRAW_FOOTER_ERROR 2 > > struct errmess { int em_number; char *em_text; } errmesses[] = > { FILE_FOOTER_ERROR, "File footer error", > DRAW_FOOTER_ERROR, "Draw footer error", > ... }; [ style compressed by me ] I occasionally have to do this kind of thing for a case where I must access the array repeatedly, and the search time is more than I care to allow. So, I define a table just as Steve does above, then define an auxilliary table of pointers to these structs that I build at runtime. This way I can lay out the table in no particular order, and the runtime sorter stuffs in all the relevant values (including a pointer to a single static "dummy" value for holes in the array). It does require a hit at runtime, but it means that I can do things like an array of signal names without having to do a *horrible* #ifdef dance that depends on my knowing which signal numbers have which values. I have seen people try to build these tables for multiple machines, and in my opinion it is ridiculous to try to do so. 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 Combat global warming -- leave the refrigerator door open
ramsey@NCoast.ORG (Cedric Ramsey) (08/20/90)
In article <499@mtndew.Tustin.CA.US> friedl@mtndew.Tustin.CA.US (Steve Friedl) writes: >Steve Summit writes: >> [Others] worry about keeping error numbers in sync with an array of >> error message strings so that the array can be indexed by the >> array number. >> >> Whenever I'm serious about making the connection between error >> numbers and messages explicit rather than implicit, I do so >> quite, er, explicitly: >> >> #define FILE_FOOTER_ERROR 1 >> #define DRAW_FOOTER_ERROR 2 >> >> struct errmess { int em_number; char *em_text; } errmesses[] = >> { FILE_FOOTER_ERROR, "File footer error", >> DRAW_FOOTER_ERROR, "Draw footer error", >> ... }; [deleted stuff] If you have to ask then it probably is ! ! !