Prev: Can there be parallel initialization of global variables in C++0x?
Next: Container adapter to treat a sorted vector as a set
From: Goran on 7 Jun 2010 18:48 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 7 Jun 2010 18:55 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 7 Jun 2010 18:55 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 8 Jun 2010 02:52
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! ] |