From: Goran on
On Jun 7, 5:34 pm, w...(a)seed.net.tw wrote:
> Take another String member function for instance:
>
> int String::insert(size_t index, const String& str);
>
> Possible error equivalents are EINVAL EFBIG ENOMEM ELOOP(str is self)
> Throwing them, one should consider the possibility of being
> misinterpreted.

One should consider the possibility that error-return will be
misinterpreted just the same. You have to show the code that you think
exhibits the problem. I'll show you the code that does exhibit the
problem if error-return is used:

// I presume this returns false in case of failure and sets errno.
bool String::insert(size_t index, const String& str);

bool f(const String& in, String& out)
{
return s2.Insert(s1);
}

String a("VERY_LONG_TEXT_HERE"), b("456");
if (!f(a, b) && errno == ENOMEM)
cerr << "Whoops, could not insert b to a because is too long";

Here, any attempt at interpreting the error at all, mine included, is
is dubious. First of all, it's not b that is long at all, it's a.
Second, there's so many other factors that play a role that even the
length of "a" is irrelevant.

I really think that you should step up to the plate and show the
situation where use of exceptions is somehow worse as a solution. So
far, you only offer vague assertions. I believe that a look at
examples with identical (or near-identical) externally observable
behavior, will show that in a __significant__ majority of cases, use
of exceptions results in more simple overall code than use of error-
return. That's why I am insisting on you to show an example. Show what
you want to do, and in majority of cases, simplest of things included,
exceptions win.

There is one, and only one overall drawback to exceptions, and that is
the speed hit (throwing is slower than error-return). If you search
this newsgroup, you can find a test cases showing what that hit is.

Goran.

P.S. ELOOP is not necessarily a failure mode. String class can easily
handle that situation.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Dragan Milenkovic on
wij(a)seed.net.tw wrote:
> The complete code for the server-like program scenario can be lengthy
> if expanded to real compilable form. But I guess my intent should be
> made clear by now.
>
> initialize();
> while (true) {
> Message msg,out;
> receive(msg);
> process(msg,out);
> send(out);
> }
>
> In general there are quite some exceptional conditions that are
> normally supposed to be handled in the server-like program loop:
>
> Reply by return (or the equivalent):
> ENOSPC no buffer space
> EINVAL this may by dynamic (e.g. format error)
> EINTR signal interrupted
> EPIPE peer down
> ETIMEDOUT preset timeout
> ENAMETOOLONG long pathname
> EFBIG too large data, similar to ERANGE
> ELOOP too many links or as mentioned above
>
> I don't see any possible catch program can be both simple and elegant
> as
> is usually declared. Note the baseline is correctness.

Don't all these error fall into few groups?

1. runtime errors in communication
2. runtime errors in parsing (marshal/unmarshal/whatever)
3. resource errors

So you design your exception hierarchy with this in mind.

Also, your algorithm is not a real-world example, yet you want
us to add exception handling that would put it into a real-world.
And why do I say this? What happens when you receive a message
but fail to send a response? THIS is where things get messy,
and NOT merely cause of the introduction of exceptions.
First, you have to design an exact algorithm, and only then
implement it using a toolset that you have at your disposal
(for example: exceptions).

In case you want a trivial solution, which would be to log
and ignore a received message if we failed to process it
or send it, then only one try-catch block enclosing
the iteration would do the trick.

--
Dragan

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Goran on
On Jun 7, 5:34 pm, w...(a)seed.net.tw wrote:
> > You should really post most comprehensive example of what complete
> > code should look, error handling and reporting included. If you do
> > that, I bet you that I can come up with a piece of code that does the
> > exact same thing, uses exceptions and is shorter in lines of "running"
> > code (basically, clean of conditional logic that will be all over the
> > place in your code). Is that OK?
>
> > Goran.
>
> The complete code for the server-like program scenario can be lengthy
> if expanded to real compilable form. But I guess my intent should be
> made clear by now.

True, it's not really appropriate to show the complete code. But it's
possible to show overall structure (see below).

>
> initialize();
> while (true) {
> Message msg,out;
> receive(msg);
> process(msg,out);
> send(out);
>
> }
>
> In general there are quite some exceptional conditions that are
> normally supposed to be handled in the server-like program loop:
>
> Reply by return (or the equivalent):
> ENOSPC no buffer space
> EINVAL this may by dynamic (e.g. format error)
> EINTR signal interrupted
> EPIPE peer down
> ETIMEDOUT preset timeout
> ENAMETOOLONG long pathname
> EFBIG too large data, similar to ERANGE
> ELOOP too many links or as mentioned above
>
> I don't see any possible catch program can be both simple and elegant
> as
> is usually declared. Note the baseline is correctness.

OK. So show some detail. How do you plan to handle error return from
receive, process, and send? Do you proceed with process if e.g.
receive fails? (I guess not). Do you "send" if "process" fails? (I
guess not.) How are receive, process and send declared? What are their
failure modes? How do you propose to signal and handle failure of
receive, process and send? You speak about possible errors, but in the
loop you've shown, there's no visible error conditions, no error
logging, no recovery, nothing. You only came up with a vague
impression that there's something wrong, but shouldn't you show, in
small snippet, what is that? People here gave you same advice, but you
as far as I can see, completely disregarded it.

Just show a rough sketch of anything using error-return, and I'll make
you a similar sketch using exceptions. It will do __exact same thing__
(that is, it will be "correct"), and there's a very big chance that it
will be shorter.

But OK, I'll give it a shot. Here's your loop with some very basic
error reporting:

// Any of these return false in case of failure
// (I presume errno is set by lower level code, but that's rather
irrelevant).
bool receive(MSG&);
bool process(const MSG&, OUT&);
bool send(const OUT&);

void log_error(const char* what)
{ // some logging.
cerr << what << " failed, errno: " << errno << endl;
}

while (true)
{
MSG msg;
if (!receive(msg))
{
log_error("receive");
continue;
}
OUT out;
if (!process(msg, out))
{
log_error("process");
continue;
}
if (!send(out))
log_error("send");
}

Or, alternatively, you could try:

while (true)
{
MSG msg;
if (receive(msg))
{
OUT out;
if (process(msg, out))
{
if (!send(out))
log_error("send");
}
else
log_error("process");
}
else
log_error("receive");
}

Here's the same thing using exceptions:

// Any of these signal error by doing throw "what" (e.g. receive will
do throw "receive").
// (Again, I presume errno is set by lower level code, but that's
rather irrelevant).
void receive(MSG&);
void process(const MSG&, OUT&);
void send(const OUT&);
void error(const char* what) { throw what; }

while (true)
try
{
MSG msg;
receive(msg);
OUT out;
process(msg, out);
send(out);
}
catch(const char* what)
{
log_error(what);
}

Now... Both snippets end up doing exact same thing, but:

1. try/catch is shorter
2. more importantly, conditional logic is out of sight; first two
variants look convoluted (and in this particular case, they indeed are
convoluted for no good reason).

Your turn to show what you think is not "correct" here.

Goran.


--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

From: Pedro LamarĂ£o on
w...(a)seed.net.tw wrote:

> The complete code for the server-like program scenario can be lengthy
> if expanded to real compilable form. But I guess my intent should be
> made clear by now.
>
> initialize();
> while (true) {
> Message msg,out;
> receive(msg);
> process(msg,out);
> send(out);
> }

I have worked on some server-link programs myself, which had the
following structure:

while (running)
{
set_up();
wait_for_events();
try
{
for_each_event_update_application();
}
catch (application_must_abort const & e)
{
disconnect_application();
}
tear_down();
}

where:

void Application::update (event e)
{
if (e means "peer disconnected") ... ;
if (e means "error") ... ;
if (e means "input available")
do_read();
if (e means "output available")
do_write();
}

where do_read and do_write are implemented in the appropriate manner
fo non-blocking Unix descriptors.

Now, as pointed out by someone else, errors such as EINTR, EAGAIN ou
EWOULDBLOCK absolutely must be handled in do_read or do_write
themselves.

EPIPE from send and return 0 from receive means end-of-file, which are
not errors in my book.

All other errors require de application to be shutdown or aborted.
In this case, observe my stack:

#3 do_read
#2 Application::update
#1 server_main_loop
#0 main

Throwing an exception which means "application must abort" has been
very useful way to traverse from frame #3 to frame #1 without
intermediate code.

The real code has one or two extra frames in the middle, making my
gain greater than what's apparent in the example.

--
P.

--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]