From: Richard Harter on
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
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
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
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
First  |  Prev  | 
Pages: 1 2 3 4 5 6 7 8 9 10
Prev: problem analysis chart
Next: Please help!