
For a library function: Some system errors are best handled by aways throwing an exception. For example, a constructor should always throw if there is no way to leave the object in a valid state when a system error is encountered. Some system errors are best handled by ignoring the error. For example, an error in a destructor. But for all the other cases, the recommended approach is to provide throwing and non-throwing versions of the function, and let the user choose which is best for a given use: whatever foo(); // always throws on error whatever foo( error_code & ec ); // never throws on error Although this works well, it has the unfortunate consequence of doubling the number of functions. That makes the interface harder to understand and is a pain to implement, document, and maintain. A refinement is for the <error_code.hpp> header to provide a dummy error_code that is interpreted as a request to thrown on errors: extern error_code throw_on_error; Then only a single signature is required: whatever foo( error_code & ec = throw_on_error ); On an error, the implementation of foo throws if ec != &throw_on_error, otherwise it sets ec to the appropriate error_code and returns without throwing. This approach gives the user the same choice, but halves the number of functions. I can't think of any downside. Comments? --Beman

Hi Beman, Beman Dawes <bdawes@acm.org> wrote:
Although this works well, it has the unfortunate consequence of doubling the number of functions. That makes the interface harder to understand and is a pain to implement, document, and maintain.
A refinement is for the <error_code.hpp> header to provide a dummy error_code that is interpreted as a request to thrown on errors:
extern error_code throw_on_error;
Then only a single signature is required: ? whatever foo( error_code & ec = throw_on_error );
On an error, the implementation of foo throws if ec != &throw_on_error, otherwise it sets ec to the appropriate error_code and returns without throwing.
This approach gives the user the same choice, but halves the number of functions. I can't think of any downside.
Comments?
I'm concerned that, even though it may halve the number of functions, it actually increases the complexity overall. Putting my standards-proposal-author hat on, it makes the specification of the behaviour of the single function more complex than the original non-throwing version. On the other hand, under the current approach the "as if" definition of the throwing version is trivial. E.g. for ip_address::to_string() string to_string() const; Effects: Calls: error_code ec; string s = to_string(ec); if (ec) throw system_error(ec); Returns: s. The increased number of code paths in the single function might also make it harder for the compiler to optimise the code. Even if you are only interested in using non-throwing functions, you could end up with the bookkeeping costs (space or time) associated with exceptions whether you like it or not. Another thought: from an implementation point of view, there's no way to ensure that the throw_on_error value is not written to accidentally (and it introduces a race condition if you do write to it). That increases the burden on the implementor. This cost potentially impacts on library users if they want to reuse the error_code facility in their own functions. While I appreciate the goal, I'm not convinced this is an improvement over the current method. I suppose my late night ramble above is really a round-about way of saying that the current error_code policy is better because it distinguishes between the two modes (throwing or non-throwing) at compile time, not run time. Cheers, Chris

Christopher Kohlhoff wrote:
Comments?
I'm concerned that, even though it may halve the number of functions, it actually increases the complexity overall.
Putting my standards-proposal-author hat on, it makes the specification of the behaviour of the single function more complex than the original non-throwing version.
The way to deal with that concern is to provide front matter that says: Unless otherwise specified, all functions taking an argument of error_code * do such-and-such. That way you don't have to say it for each function individually.
The increased number of code paths in the single function might also make it harder for the compiler to optimise the code. Even if you are only interested in using non-throwing functions, you could end up with the bookkeeping costs (space or time) associated with exceptions whether you like it or not.
Yes, that is a concern.
Another thought: from an implementation point of view, there's no way to ensure that the throw_on_error value is not written to accidentally (and it introduces a race condition if you do write to it). That increases the burden on the implementor. This cost potentially impacts on library users if they want to reuse the error_code facility in their own functions.
If we follow Guillaume's suggestion and use a pointer, that problem goes away. And I don't think we have much choice; comparing addresses across DLL boundaries has gotten me in trouble in the past so I think we have to avoid that.
While I appreciate the goal, I'm not convinced this is an improvement over the current method. I suppose my late night ramble above is really a round-about way of saying that the current error_code policy is better because it distinguishes between the two modes (throwing or non-throwing) at compile time, not run time.
Yes, and AFAIK there is no need to change behavior at runtime. Let me think about it a bit. You may be right. Thanks, --Beman

Le lundi 28 août 2006 à 09:49 -0400, Beman Dawes a écrit :
extern error_code throw_on_error;
Then only a single signature is required:
whatever foo( error_code & ec = throw_on_error );
On an error, the implementation of foo throws if ec != &throw_on_error, otherwise it sets ec to the appropriate error_code and returns without throwing.
I often use this idiom and I like it. You may however encounter problems on systems that have difficulties mixing static and shared libraries. It may be safer to stick with this version of the idiom: whatever foo(error_code *ec = NULL); Best regards, Guillaume

On 8/28/06, Guillaume Melquiond <guillaume.melquiond@ens-lyon.fr> wrote:
Le lundi 28 août 2006 à 09:49 -0400, Beman Dawes a écrit :
extern error_code throw_on_error;
Then only a single signature is required:
whatever foo( error_code & ec = throw_on_error );
On an error, the implementation of foo throws if ec != &throw_on_error, otherwise it sets ec to the appropriate error_code and returns without throwing.
I often use this idiom and I like it.
You may however encounter problems on systems that have difficulties mixing static and shared libraries.
Argh! And a bunch of other words unfit for a public mailing list.
It may be safer to stick with this version of the idiom:
whatever foo(error_code *ec = NULL);
Agreed. Thanks, --Beman

Beman Dawes wrote:
For a library function:
Some system errors are best handled by aways throwing an exception. For example, a constructor should always throw if there is no way to leave the object in a valid state when a system error is encountered.
Some system errors are best handled by ignoring the error. For example, an error in a destructor.
I think that it is the responsibility of the user to make sure destructors they write don't leak exceptions, and in general to implement the error-handling, given the specifications of the library. It is the responsibility of the library implementer to decide whether a specific function should throw to indicate a failure, or whether returning an error code is more appropriate. Otherwise, we shift the responsibility for this decision on the user, and typically they are no experts in the problem domain of the library.
But for all the other cases, the recommended approach is to provide throwing and non-throwing versions of the function, and let the user choose which is best for a given use:
whatever foo(); // always throws on error whatever foo( error_code & ec ); // never throws on error
I, as a user of the library which gives me this option, would be disappointed if I have to use such an interface. Note that even if the library always throws under certain condition, I can easily write code to prevent such exceptions from propagating in contexts where they would cause problems, such as in destructors or at a DLL boundary.
Although this works well, it has the unfortunate consequence of doubling the number of functions. That makes the interface harder to understand and is a pain to implement, document, and maintain.
Indeed.
A refinement is for the <error_code.hpp> header to provide a dummy error_code that is interpreted as a request to thrown on errors:
extern error_code throw_on_error;
Then only a single signature is required:
whatever foo( error_code & ec = throw_on_error );
On an error, the implementation of foo throws if ec != &throw_on_error, otherwise it sets ec to the appropriate error_code and returns without throwing.
What if the user wants some failures in foo() to be reported as errors, and others to be reported by throwing an exception? There are contexts in which this is *exactly* what the user may want. I don't think that it is any less reasonable to support this than to support the option for users to decide whether they want exceptions from a given function in the first place. --Emil Dotchevski
participants (4)
-
Beman Dawes
-
Christopher Kohlhoff
-
Emil Dotchevski
-
Guillaume Melquiond