Boost logo

Boost :

From: Richard Peters (R.A.Peters_at_[hidden])
Date: 2002-08-02 06:03:21


David Brownstein wrote:
> Thanks for your response, I find your perspective to be very
> interesting.
>
>> IMHO, this is completely the wrong direction. It always bothers me
>> if return values are misused as error codes. When a function returns
>> a value, it should be the result of a calculation, that is what
>> functions do.
> I disagree- many functions perform operations. For example, I hardly
> think that the sockets function select() could be thought of as a
> "calculation". Is writing a file a "calculation"?

You are right, they aren't functions, they're operations, or procedures (in my
view). I find this the most annoying thing in C/C++. I usually see 'void
something(params)' as a procedure, not as a function returning void. I like Pascal's
'procedure ...' better. (though in templates, C++'s form can be quite convenient)
I'm not that much of a purist that I think a function shouldn't have side effects.
The result of a function (ie, T something(params) with T != void) should reflect
something about its parameters, though. IMO, errors don't belong to this.
A function may have side effects, but it shouldn't consist of side effects alone.
I'd rather write void DoSomething(char& somevalue) in those cases.
If you write a = DoSomething(), you think "Hey! Something happens to a!"
If you write DoSomething(a), you think "Hey! Something happens!" and when you look
closer, you see that also something happens with a, but that that thing is less
significant to the purpuse of the function. The most important thing that happens
here are the things in DoSomething, not the value of a.

>> Your firts argument might be true, some people do care about
>> performance.
> I do not,
> I was thinking especially about cases where functions (often OS API
> functions) are frequently expected to return an "error" condition,
> and in common usage are called repeatedly from a loop. An example
> would be a loop that calls select() on a socket, specifying a timeout
> value so the call doesn't block, and then if there isn't anything in
> the socket buffer to read, continues doing some processing before
> coming back to the select() call again.

When select() times out, it returns 0. That's exactly its postcondition: The select
function returns the total number of socket handles that are ready and contained in
the FD_SET structures.
When an error occurs in select() (which almost never happens, IIRC), it returns -1,
an error. If it threw an exception, your code would be pretier, you just load the
result of select into a counter, and check for counter times your FD_SET. Now you
have to add code for -1 _at the wrong place_ in your code. You can choose where to
put your exception handlers, you can't choose where to put your error handling code.

> A similar scenario is
> (often?) used WRT non-blocking waitXXX() calls on an event handle to
> see if an event has been signaled. In both cases there are (often
> better) asynchronous methods of performing the same operations, but
> that's not the point. The point is that it is both valid and common
> for funcions to return error values, and 1) using exceptions here
> would be somewhat clumsy to implement, and 2) very onerous from a
> performance stand-point, where the exception might be thrown
> thousands of times per second inside the loop! I think in such cases
> the difference between "normal" (or "valid" from your perspective)
> and "error" is questionable. Anyway I would argue that returning an
> error code IS a valid use of a function return. It also has a long
> history of such use in the standard C and C++ libraries, among
> others.

I don't like the long history of returning errors :) Anyway, I believe that in these
cases it is valid to return an error, as this is the low-level library. I still
don't think that the Filesystem library should use that method, though.

>> When a function can't fullfill its
>> postcondition, it has to report that as an error. If you want to
>> return a special errorcode, you have to widen the postcondition,
>> which makes it harder for programmers to use the function. This is
>> much more readable/usable: Postcondition: !exists( ph )
>> Compared to this:
>> Postcondition: !exists( ph ) || return_value != 0
>> And you aren't ready there, because you should add things like
>> Postcondition: !exists( ph ) || (return_value != 0 &&
>> (return_value == -1 when file not found) && (return_value == -2 when
>> some other error) etc.
> I agree with you. Error handling is messy, whether you handle it with
> a return value or by throwing an exception. Either way you have to
> write the similar code to handle the problem. I'm simply trying to
> combine the best of both techniques.
>
>> I don't think that your second argument holds.
> I agree that it is weak. It was poorly expressed. What I was thinking
> of is that sometimes a processing error doesn't matter, and can
> simply be ignored. If the error is "signaled" by returning an error
> code, then the caller can choose to ignore the return value. This is
> harder to do when the error is signaled via an exception!
>
> Thanks,
> David

True... Maybe adding a parameter ..., useexceptions = true) to the function solves
it? I don't think I like this method either... Maybe only use that extra parameter
for only certain cases (like suppressing an exception for deleting a file that
didn't exist, but don't provide parameters to suppress all other exceptions). Any
other options?
Oh and I thought about making a function ignore_exception<type> but I don't know if
it's possible/convenient to use. My idea was something like:

template <class E, class T>
T ignore_exception(function<T> f) { try { f(); } catch (const E&) {} }
template <class E, class T, class P1>
T ignore_exception(function<T, P1> f, P1 p1) { try { f(p1); } catch (const E&) {} }
etc.
Its use would be something like ignore_exception<std::runtime_error>(&func, x);
I just tested it, with this version you'd have to use
ignore_exception<std::runtime_error, void, int>(&func, x); then it works. Is there a
possibility that you don't need , void, int?

Best regards,

Richard Peters


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk