Prev: problem analysis chart
Next: Please help!
From: Richard Harter on 13 Nov 2007 12:29 On Mon, 12 Nov 2007 11:11:12 -0800, Paul Hsieh <websnarf(a)gmail.com> wrote: [snip excellent comments] I appreciate your comments for which many thanks. The discussion has moved on a bit from the post that you commented upon. I have a few general thoughts here. My theory is that service routines should do error detection and report errors. I opine that placing the onus of error detection on the user of the service leads to a lot of unnecessary replication of code and all, too often, failure to actually test for errors. Your suggestions for generalizing are interesting. In the implementation I think it best to distinguish between different kinds of "streams". Offering the potential for different kinds of separator characters is probably a good idea. One final thought: just because one has created a "swiss army knife" utility as a basis doesn't mean that one is committed to using it in all its multifaceted glory. One can always write wrappers. Richard Harter, cri(a)tiac.net http://home.tiac.net/~cri, http://www.varinoma.com In the fields of Hell where the grass grows high Are the graves of dreams allowed to die
From: Richard Harter on 14 Nov 2007 12:48 On Thu, 11 Oct 2007 23:05:29 GMT, cri(a)tiac.net (Richard Harter) wrote: [snip original post] Here (unless I am persuaded otherwise) is my final take on the interface. Many thanks to all that commented. The getfline.h include file defines four things: (1,2) There are two variables, flags and errno, that replace the status word (they will be inside a structure.) They are supported by two enumerated types called gfl_flags and gfl_errors. These replace the macro definitions. After some thought I decided that bit fields were a bad idea. The enumerated types still invade the user space but in a nicer way. The gfl_flags enum declaration looks like this: enum gfl_flags { gfl_clean = 0x1, gfl_eofok = 0x2, gfl_cut = 0x4, gfl_trunc = 0x8, gfl_omit = 0x20, gfl_exit = 0x40, gfl_log = 0x80, gfl_nomax = 0x100 }; The gfl_flags type expresses the same values as the corresponding macros and are to be used in the same way. For example cb.flags = gfl_eofok | gfl_cut | gfl_log; The "nomax" flag is an addition. It turns off bounds checking. The "clean" flag is a replacement for the "TRANS" flag. What is happening here is that the default is changed to transient copies. The gfl_errors enum declaration looks like this: enum gfl_errors { gfl_ok = 0, gfl_stream, gfl_bufsize, gfl_flags, gfl_io, gfl_storage, gfl_corrupt }; The usage is different from before. The errno variable will have one of these values. Most of the argument errors are no longer needed. The gfl_corrupt return has been added because it may be possible for to corrupt the hidden state. The get_ok value for errno says that was no error; a positive value says that there was an error. (3) A structure that looks like this: struct gfl_cb { struct gfl_private * pvt; size_t length; gfl_errors errno; size_t maxlen; int flags; }; The gfl_private struct is new; it is an opaque pointer to a struct created by getfline that is used to hold the state data from one call to the next. The gfl_cb struct does not have to be used (see prototypes below) but if it is it should be populated as follows: struct gfl_cb = {0,0,0,MAXLEN,FLAGS}; where MAXLEN is the maximum line length permitted, and FLAGS is set as the or of the selected flags. MAXLEN may be 0 if the nomax flag is selected. Arguments two and three don't need initializing,; setting them to zero might be good practice. The pvt pointer must be 0 (NULL) when the first line is extracted; if it is not bad things may happen. It will be cleared by getfline after the last line is extracted. (4,5) Two prototypes that look like this: char * getfline (FILE * fptr, struct gfl_cb * cb); int gfl_terminate ( struct gfl_cb * cb); Getfline is the principle function. If the second argument is NULL getfline will run in clean copy mode with no bounds check and will ignore a missing final EOL. It is the appropriate choice for simple usage. If the second argument is not NULL it must be correctly initiialized. Gfl_terminate is there for a special case. Ordinarily getfline will clean up its private data after the last line is extracted. However the user may break out of the read loop before the last line is read. If so, gfl_terminate should be called to clean up the private data. Failure to do is not fatal; however it will leak some memory. Return of zero is okay, nonzero is an error. Example usage: while (line = getfline(fptr, &cb)) { ... do stuff ... if (something_happened) { if (gfl_terminate(cb)) die_horribly(); break; } ... do other stuff ... } Richard Harter, cri(a)tiac.net http://home.tiac.net/~cri, http://www.varinoma.com In the fields of Hell where the grass grows high Are the graves of dreams allowed to die
From: CBFalconer on 12 Nov 2007 16:57 Richard Harter wrote: > <cbfalconer(a)yahoo.com> wrote: >> Richard Harter wrote: >>> >>... snip ... >>> >>> where I assume that the return is 0 if a line was read properly >>> and a code value if it fails in some way. This adds error >>> returns for a modest price in complexity. It has the same built >>> in automatic inefficiencies. It also steps on one of C's little >>> awkwardnesses, namely the convention that 0 is false and non-zero >>> is true. IMNSHO this is an ancient mistake; generally speaking >>> there usually is only one way to succeed and many ways to fail. >>> It would have better to do it the other way around. However it >>> is a convention cast is three billion year old granite so there >>> is nothing to be done about it. >> >> No, the thing is that there is only one signal needed for 'OK', but >> it is quite possible to return various error forms. The 0 == OK >> matches this. It is a general practice in the standard library, >> and avoids returning a separate error value. > > I opine you're missing the point. Of course one can do all sorts > of things but a common C idiom is > > while (somefunc(/* args */)) {/*body */} > > If 0 were true and non zero false you could have all sorts of > termination codes. The convention would match the idiom. What > you did is of the form > > while (somefunc(...) == SUCCESS_CODE)) {...} > > which, while correct C, is clumsier and somewhat of a hack. As long as 0 reports no error, you use: while (!(err = somefunc(...))) { ... } if (err != DONE) fixupcode(); :-) -- Chuck F (cbfalconer at maineline dot net) <http://cbfalconer.home.att.net> Try the download section. -- Posted via a free Usenet account from http://www.teranews.com
From: David Thompson on 25 Nov 2007 18:40
On Mon, 12 Nov 2007 16:05:36 GMT, cri(a)tiac.net (Richard Harter) wrote: > On Mon, 12 Nov 2007 02:17:13 GMT, David Thompson > <dave.thompson2(a)verizon.net> wrote: > > [snip] > > >You can do > > while( (fg = fgetline (...)) . status == 0 ){ ... } > > /* or != 0, or > 0, or whatever your semantics is */ > > I thought of this, but I have never used it and didn't know > whether it would work. Is there a reason you put spaces around > the dot? > Only to emphasize it as the solution to the issue posed. They aren't required lexically, nor are the others in this (small) example. If I did/do use this in real code I would probably keep the space on the left since the left operand (in this case) is a moderately complex subexpression and I like it visually separated; I might well drop the space on the right and use (fg = blah) .status == 0 But that's only style and I wouldn't object to dropping both. - formerly david.thompson1 || achar(64) || worldnet.att.net |