[system] system_error first draft

The filesystem proposal accepted by the LWG for TR2 also includes a <system_error> header with a bit of error reporting machinery. In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti... The plan is to use these classes uniformly in TR2. Presumably we will want Boost implementations, and use them in other Boost libraries besides Boost.Filesystem. Because there are likely to be other small operating system dependent components in the future, I'm suggesting that they go in a separate Boost.System library. I'm about to post a CPU timer component that would also fit nicely into Boost.System IMO. There is an early version of Boost.System stuff in the vault. Go to the System directory at http://boost-consulting.com/vault/ for system-0.1.zip. The preliminary error_code/system_error docs can be viewed online at http://mysite.verizon.net/beman/system_error.html Note some changes from the TR2 version: * Use of the unspecified-bool-type idom to test an error_code object to see if there is a non-zero error code. * Name changes. The problem with naming is that there are functions whose most obvious name is "errno", given the long C, C++, and POSIX tradition. But 17.4.1.2 of the C++ standard specifically says that errno shall be a macro! Boo. Hiss. So that name is out. I'm very curious to hear if people think the newer names are an improvement over the TR2 versions. And any other comments are welcome! --Beman

"Beman Dawes" wrote:
There is an early version of Boost.System stuff in the vault. Go to the System directory at http://boost-consulting.com/vault/ for system-0.1.zip.
The preliminary error_code/system_error docs can be viewed online at http://mysite.verizon.net/beman/system_error.html
The catch (...) in what() doesn't make much sense - the app would be likely terminated at the moment. A fixed size char[] array could be used w/o danger of throwing (as it is in STLport). Perhaps individual, platform dependent exceptions for relevant errors would be even more handy for users, ala: class system_error_ERROR_WRITE_PROTECT : public system_error Number of such miniclasses would be limited, the classes could be added as needed, fallback will always exist Portable applications would use only the base. The few classes where the error means the same on all platforms (Win32: ERROR_FILE_NOT_FOUND, errno: ENOFILE) could have common parent. ------------ In process_exec.hpp two handles leak if timeout expires. The thread handle could be closed immediatelly after process starts. /Pavel

On Sun, 25 Jun 2006 10:00:42 -0400, Beman Dawes <bdawes@acm.org> wrote:
The filesystem proposal accepted by the LWG for TR2 also includes a <system_error> header with a bit of error reporting machinery.
Great.
In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti...
I've just looked at this paper, but not at the code (I'm working contemporarily on three things now, so better I don't add a fourth :)). Here are my "off the top of my head" remarks: * something I've remembered of when seeing the names "system_error", "error_code": why not "file_system"? (maybe this was already discussed in the filesystem library review, but I don't recall) * the name system_error_type is a bit misleading to me, as it seems to refer to the type of system_error (i.e. system_error :)) not to the type used by the OS API. Maybe it could be renamed to native_system_error_type or something like that? * please, don't "hardcode" the usage of std::string and std::wstring; I'm noticing this is happening everywhere in the standard, including TR1, TR2 and one of the library issues about std::bitset<>; as James Kanze made me notice, there's no conceptual reason why strings and std::bitsets (or system_error, of fstreams) should know about each other: if one wants the "textual representation" of an object the idiom to use is operator<<, which is also a standard "name" suitable for generic programming; obtaining the textual representation is a matter of formatting and there's no reason why one should have e.g. to_string() rather than to_ber_encoding() or to_xml(). Not to speak of the fact that a conversion to string may require a locale object, which you automatically get if using a stream. Just to make an example: supposing one want to implement ipv4::to_string(), what should be done with octets whose value is less than 100 or less than 10? a) 192.168.0.10 b) 192.168.0.010 c) 192.168.000.010 .... As you see, this is formatting. This is a Java design error that C++ should not repeat (think also of Java's hashCode() -that's not different from to_string(), actually; a class shouldn't know about strings more than it knows about hash codes) * "The class error_code defines the type of objects used to identify specific errors originating from the operating system." Who says this is a numeric code? It could be a handle, for instance, or any kind of opaque object. Why not calling the class "error_id"? * many constructors are not explicit; is that intentional? * system_message is a particular beast: since it is likely to be used in very unstable situations I think the remark about avoiding std::basic_string holds even more. Here I would definitely use an array of chars. Of course the standard doesn't usually say how you should implement the class, but in this case it should make clear that std::string cannot be used; there should IMHO be no append and probably the message formatting should be a nothrow operation --Gennaro.

"Gennaro Prota" wrote:
* "The class error_code defines the type of objects used to identify specific errors originating from the operating system."
Who says this is a numeric code? It could be a handle, for instance, or any kind of opaque object. Why not calling the class "error_id"?
All common operating systems (Windows, Posix) return error as an integer value and I think this is true for embedded OS' as well. /Pavel

Gennaro Prota wrote:
On Sun, 25 Jun 2006 10:00:42 -0400, Beman Dawes <bdawes@acm.org> wrote:
The filesystem proposal accepted by the LWG for TR2 also includes a <system_error> header with a bit of error reporting machinery.
Great.
In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti...
I've just looked at this paper, but not at the code (I'm working contemporarily on three things now, so better I don't add a fourth :)). Here are my "off the top of my head" remarks:
* something I've remembered of when seeing the names "system_error", "error_code": why not "file_system"? (maybe this was already discussed in the filesystem library review, but I don't recall)
* the name system_error_type is a bit misleading to me, as it seems to refer to the type of system_error (i.e. system_error :)) not to the type used by the OS API. Maybe it could be renamed to
native_system_error_type
or something like that?
I was also quite unhappy with the names in the proposal to the committee. Thus the version in the Boost vault has what I hope are improved names, less likely to cause confusion. If Boosters like the new names, I'll propose them to the committee. The intent is synchronize both versions so they use the same names. So please looks at the new names and see if you think they are an improvement.
* please, don't "hardcode" the usage of std::string and std::wstring;
When dealing directly with the operating system, it is hard to see how to avoid that. Please be a bit more specific, and suggest alternatives.
I'm noticing this is happening everywhere in the standard, including TR1, TR2 and one of the library issues about std::bitset<>; as James Kanze made me notice, there's no conceptual reason why strings and std::bitsets (or system_error, of fstreams) should know about each other: if one wants the "textual representation" of an object the idiom to use is operator<<, which is also a standard "name" suitable for generic programming; obtaining the textual representation is a matter of formatting and there's no reason why one should have e.g. to_string() rather than to_ber_encoding() or to_xml(). Not to speak of the fact that a conversion to string may require a locale object, which you automatically get if using a stream.
Just to make an example: supposing one want to implement ipv4::to_string(), what should be done with octets whose value is less than 100 or less than 10?
a) 192.168.0.10 b) 192.168.0.010 c) 192.168.000.010 ....
As you see, this is formatting.
This is a Java design error that C++ should not repeat (think also of Java's hashCode() -that's not different from to_string(), actually; a class shouldn't know about strings more than it knows about hash codes)
That is beyond the scope of anything I can deal with here. You need to write a paper for the committee, identifying specific places in the standard (or TR1/TR2) where you think that is happening, and where possible suggest solutions to the problems you identify.
* "The class error_code defines the type of objects used to identify specific errors originating from the operating system."
Who says this is a numeric code? It could be a handle, for instance, or any kind of opaque object. Why not calling the class "error_id"?
The native operating system's sysno_t is implementation defined. I'll take a look, but it should be possible for it to be something other than a numeric type.
* many constructors are not explicit; is that intentional?
I'm not sure which constructors you are looking at. The only converting constructor I see is already explicit.
* system_message is a particular beast: since it is likely to be used in very unstable situations I think the remark about avoiding std::basic_string holds even more. Here I would definitely use an array of chars.
Of course the standard doesn't usually say how you should implement the class, but in this case it should make clear that std::string cannot be used; there should IMHO be no append and probably the message formatting should be a nothrow operation
I'll review that. You may be right. Thanks, --Beman

On Sun, 25 Jun 2006 15:25:45 -0400, Beman Dawes <bdawes@acm.org> wrote:
Gennaro Prota wrote:
On Sun, 25 Jun 2006 10:00:42 -0400, Beman Dawes <bdawes@acm.org> wrote:
[...] In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti...
I've just looked at this paper, but not at the code (I'm working contemporarily on three things now, so better I don't add a fourth :)).
Well, for today I've finished with dynamic_bitset so I'm having a second look at this :)
Here are my "off the top of my head" remarks:
* something I've remembered of when seeing the names "system_error", "error_code": why not "file_system"? (maybe this was already discussed in the filesystem library review, but I don't recall)
I'm still curious about that (since filesystem was introduced in boost, actually).
* the name system_error_type is a bit misleading to me, as it seems to refer to the type of system_error (i.e. system_error :)) not to the type used by the OS API. Maybe it could be renamed to
native_system_error_type
or something like that?
I was also quite unhappy with the names in the proposal to the committee. Thus the version in the Boost vault has what I hope are improved names, less likely to cause confusion.
Well, sysno_t is IMHO better than system_error_type because it a) doesn't cause any fallacious mental association with system_error and because b) it's consistent with errno_t. That said, the wording at the url you provide still perplexes me: Type system_error_type [now sysno_t] is the implementation-defined type used by the operating system to report error codes. It seems to me (please correct me if I'm wrong) that this refers to the type actually used by the operating system, not to an integer type providing a mapping to numbers. Well, it could even by in turn a mapping but I don't see why casting in stone that it must be an integer. Though I don't know of any system which doesn't use numbers for that, I still think other types are perfectly reasonable. You know very well that if I return 0 from main, or call exit(0) it's the implementation responsibility to translate 0 to the implementation-defined form to indicate successful completion. Plan 9's exit() actually returns a string :) Then a POSIX compatibility environment (APE) is available that implements exit(int) -i.e. provides the mapping required by the C standard. This is just an example to show that _under the hood_ of the C implementation things can actually be different and require a mapping. Does the sentence above want what is under the hood or what the C or C++ implementation exposes? My preferred name, FWIW, is still native_system_error[_type].
If Boosters like the new names, I'll propose them to the committee. The intent is synchronize both versions so they use the same names.
So please looks at the new names and see if you think they are an improvement.
* please, don't "hardcode" the usage of std::string and std::wstring;
When dealing directly with the operating system, it is hard to see how to avoid that. Please be a bit more specific, and suggest alternatives.
I didn't mean hardcoding character literals and such (not that I've noticed that in your code). I meant not using basic_string/string/wstring at all, because that creates unnecessary coupling with the string library and is a less flexible and generic<> approach than iostreams. I've thought a lot about this, and the more time elapses the more I get convinced. Let's consider, for instance, the case of std::bitset<>. What if I want my bitset represented as ttftt [t=true, f=false] instead of 11011 And why not xx0xx ? Of course to_string() cannot do that, unless one wants to add a pletora of extra parameters. The stream approach instead is locale aware and allows any kind of special formatting. And it is highly customizable. As soon as I can, I'll provide a little helper class to aid in customizing input and output of dynamic_bitset as indicated above. My example about ipv4 should make the point even clearer: in general there's no unique way to represent an object as a sequence of characters, and what we actually want when doing conversion to string is *formatting*. A sequence of characters is just a special form of textual representation. BTW, once inserters and extractors work correctly you can use lexical_cast<> for simple, raw, text conversion needs. With the iostream approach you have a clear separation between a) the class which contains the data b) the textual representation of those data c) a generic name, operator <<, or operator >> to connect them and convert to and from character representations.
I'm noticing this is happening everywhere in the standard, including TR1, TR2 and one of the library issues about std::bitset<>; as James Kanze made me notice, there's no conceptual reason why strings and std::bitsets (or system_error, of fstreams) should know about each other: if one wants the "textual representation" of an object the idiom to use is operator<<, which is also a standard "name" suitable for generic programming; obtaining the textual representation is a matter of formatting and there's no reason why one should have e.g. to_string() rather than to_ber_encoding() or to_xml(). Not to speak of the fact that a conversion to string may require a locale object, which you automatically get if using a stream.
Just to make an example: supposing one want to implement ipv4::to_string(), what should be done with octets whose value is less than 100 or less than 10?
a) 192.168.0.10 b) 192.168.0.010 c) 192.168.000.010 ....
As you see, this is formatting.
This is a Java design error that C++ should not repeat (think also of Java's hashCode() -that's not different from to_string(), actually; a class shouldn't know about strings more than it knows about hash codes)
That is beyond the scope of anything I can deal with here. You need to write a paper for the committee, identifying specific places in the standard (or TR1/TR2) where you think that is happening, and where possible suggest solutions to the problems you identify.
Would a DR be suitable? I was specifically alluding, for instance to lib DR 434: <http://google.com/group/comp.std.c++/browse_thread/thread/188ddd204ebd67da/739035f50ae29ab8?#739035f50ae29ab8> and to <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1475.html#15>
* "The class error_code defines the type of objects used to identify specific errors originating from the operating system."
Who says this is a numeric code? It could be a handle, for instance, or any kind of opaque object. Why not calling the class "error_id"?
The native operating system's sysno_t is implementation defined. I'll take a look, but it should be possible for it to be something other than a numeric type.
* many constructors are not explicit; is that intentional?
I'm not sure which constructors you are looking at. The only converting constructor I see is already explicit.
I was again looking at the paper, not at your code. I'm sure you istinctively used explicit where it made sense. --Gennaro.

"Gennaro Prota" <gennaro_prota@yahoo.com> wrote in message news:njvt925vi49uk11gclqrj23ql76hbu3sip@4ax.com...
On Sun, 25 Jun 2006 15:25:45 -0400, Beman Dawes <bdawes@acm.org> wrote:
Here are my "off the top of my head" remarks:
* something I've remembered of when seeing the names "system_error", "error_code": why not "file_system"? (maybe this was already discussed in the filesystem library review, but I don't recall)
I'm still curious about that (since filesystem was introduced in boost, actually).
AFAICR, the question never came up. I can't remember why I started calling it filesystem rather than file_system, but suspect I was influenced by other material I read at the time. Try googling for "filesystem", and you can see some examples.
That said, the wording at the url you provide still perplexes me:
Type system_error_type [now sysno_t] is the implementation-defined type used by the operating system to report error codes.
It seems to me (please correct me if I'm wrong) that this refers to the type actually used by the operating system, not to an integer type providing a mapping to numbers. Well, it could even by in turn a mapping but I don't see why casting in stone that it must be an integer. Though I don't know of any system which doesn't use numbers for that, I still think other types are perfectly reasonable.
You know very well that if I return 0 from main, or call exit(0) it's the implementation responsibility to translate 0 to the implementation-defined form to indicate successful completion. Plan 9's exit() actually returns a string :) Then a POSIX compatibility environment (APE) is available that implements exit(int) -i.e. provides the mapping required by the C standard. This is just an example to show that _under the hood_ of the C implementation things can actually be different and require a mapping. Does the sentence above want what is under the hood or what the C or C++ implementation exposes?
It is whatever the operating system's API uses to report errors. If there is a layer between the C++ program and the operating system, like cygwin, it is what that middle layer's API exposes.
My preferred name, FWIW, is still native_system_error[_type].
Seems too wordy to me.
* please, don't "hardcode" the usage of std::string and std::wstring;
When dealing directly with the operating system, it is hard to see how to avoid that. Please be a bit more specific, and suggest alternatives.
I didn't mean hardcoding character literals and such (not that I've noticed that in your code). I meant not using basic_string/string/wstring at all, because that creates unnecessary coupling with the string library and is a less flexible and generic<> approach than iostreams. I've thought a lot about this, and the more time elapses the more I get convinced. Let's consider, for instance, the case of std::bitset<>. What if I want my bitset represented as
ttftt [t=true, f=false]
instead of
11011
And why not
xx0xx
? Of course to_string() cannot do that, unless one wants to add a pletora of extra parameters. The stream approach instead is locale aware and allows any kind of special formatting. And it is highly customizable. As soon as I can, I'll provide a little helper class to aid in customizing input and output of dynamic_bitset as indicated above. My example about ipv4 should make the point even clearer: in general there's no unique way to represent an object as a sequence of characters, and what we actually want when doing conversion to string is *formatting*. A sequence of characters is just a special form of textual representation.
BTW, once inserters and extractors work correctly you can use lexical_cast<> for simple, raw, text conversion needs.
With the iostream approach you have a clear separation between a) the class which contains the data b) the textual representation of those data c) a generic name, operator <<, or operator >> to connect them and convert to and from character representations.
I'm not seeing how this applies to class error code (which does not use strings at all), unless you are suggesting that stream inserters and extractors be provided.
I'm noticing this is happening everywhere in the standard, including TR1, TR2 and one of the library issues about std::bitset<>; as James Kanze made me notice, there's no conceptual reason why strings and std::bitsets (or system_error, of fstreams) should know about each other: if one wants the "textual representation" of an object the idiom to use is operator<<, which is also a standard "name" suitable for generic programming; obtaining the textual representation is a matter of formatting and there's no reason why one should have e.g. to_string() rather than to_ber_encoding() or to_xml(). Not to speak of the fact that a conversion to string may require a locale object, which you automatically get if using a stream.
Just to make an example: supposing one want to implement ipv4::to_string(), what should be done with octets whose value is less than 100 or less than 10?
a) 192.168.0.10 b) 192.168.0.010 c) 192.168.000.010 ....
As you see, this is formatting.
This is a Java design error that C++ should not repeat (think also of Java's hashCode() -that's not different from to_string(), actually; a class shouldn't know about strings more than it knows about hash codes)
That is beyond the scope of anything I can deal with here. You need to write a paper for the committee, identifying specific places in the standard (or TR1/TR2) where you think that is happening, and where possible suggest solutions to the problems you identify.
Would a DR be suitable? I was specifically alluding, for instance to lib DR 434:
and to
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1475.html#15>
It seems to me that a full paper, including the specific working paper changes you are suggesting would be more likely to succeed than a simple DR.
* many constructors are not explicit; is that intentional?
I'm not sure which constructors you are looking at. The only converting constructor I see is already explicit.
I was again looking at the paper, not at your code. I'm sure you istinctively used explicit where it made sense.
Please indicate the specific constructors. It is certainly possible that some that should be explicit aren't, and need to be changed. Thanks, --Beman

Hi Beman, apologies for the very late reply. I postponed it and then always forgot :(
AFAICR, the question never came up. I can't remember why I started calling it filesystem rather than file_system, but suspect I was influenced by other material I read at the time. Try googling for "filesystem", and you can see some examples.
Seen. Both forms are common; at the moment, though, Google's search results for "file system" (in quotes) are almost twice as much those for filesystem (about 60,000,000 vs. 30,000,000). Any hope for it to become "file_system"?
That said, the wording at the url you provide still perplexes me:
Type system_error_type [now sysno_t] is the implementation-defined type used by the operating system to report error codes.
[...]
It is whatever the operating system's API uses to report errors. If there is a layer between the C++ program and the operating system, like cygwin, it is what that middle layer's API exposes.
Ok. So the wording needs some tweak (I know it is difficult to do in standardese, sorry for that).
My preferred name, FWIW, is still native_system_error[_type].
Seems too wordy to me.
Now that you told about middle layers being considered, what about "system_api_error"? Or should it necessarily have _t/_type at the end?
[...]
I'm not seeing how this applies to class error code (which does not use strings at all), unless you are suggesting that stream inserters and extractors be provided.
I'm a bit lost in all the renaming we have done so far :) Anyhow, at the url you provided: <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnostics-library> the class system_error has the following constructor: system_error(const std::string & what_arg, error_code ec);
[snipped suggestion to file a full paper]
I'm not sure which constructors you are looking at. The only converting constructor I see is already explicit.
As I said I was only looking at the online document; there error_code(system_error_type err); is not declared explicit. -- [ Gennaro Prota, C++ developer for hire ] [ resume: available on request ]

Gennaro Prota <gennaro_prota@yahoo.com> writes:
AFAICR, the question never came up. I can't remember why I started calling it filesystem rather than file_system, but suspect I was influenced by other material I read at the time. Try googling for "filesystem", and you can see some examples.
Seen. Both forms are common; at the moment, though, Google's search results for "file system" (in quotes) are almost twice as much those for filesystem (about 60,000,000 vs. 30,000,000). Any hope for it to become "file_system"?
Maybe, but I think "filesystem" might be more specific than "file system." -- Dave Abrahams Boost Consulting www.boost-consulting.com

Beman Dawes <bdawes@acm.org> wrote:
I'm very curious to hear if people think the newer names are an improvement over the TR2 versions.
What about maybe ditching the member functions sysno_value() and errno_value() entirely and instead having additional free function overloads: sysno_t to_sysno(error_code ec); errno_t to_errno(error_code ec); And the only way to set an error code is by assignment: ec = 1234; // sysno ec = error_code(1234, from_errno); Some quick notes on the implementation (sorry for the noise if someone has already mentioned them): - I notice that you only use strerror() on POSIX platforms. On some platforms this is not threadsafe, and strerror_r() needs to be used instead. From memory this function's behaviour can differ a bit between various platforms as well. - The ::LocalFree() usage isn't exception safe. See: <http://boost.cvs.sourceforge.net/boost/boost/boost/asio/system_exception.hpp?revision=1.1&view=markup> in the implementation of what() for an example. Cheers, Chris

"Christopher Kohlhoff" <chris@kohlhoff.com> wrote in message news:20060626135454.2789.qmail@web32609.mail.mud.yahoo.com...
Beman Dawes <bdawes@acm.org> wrote:
I'm very curious to hear if people think the newer names are an improvement over the TR2 versions.
What about maybe ditching the member functions sysno_value() and errno_value() entirely and instead having additional free function overloads:
sysno_t to_sysno(error_code ec); errno_t to_errno(error_code ec);
And the only way to set an error code is by assignment:
ec = 1234; // sysno ec = error_code(1234, from_errno);
That would simplify the interface, but I'm worried about ease-of-use in typical use scenarios. Ease-of-use was the motivation for the current design.
Some quick notes on the implementation (sorry for the noise if someone has already mentioned them):
- I notice that you only use strerror() on POSIX platforms. On some platforms this is not threadsafe, and strerror_r() needs to be used instead. From memory this function's behaviour can differ a bit between various platforms as well.
- The ::LocalFree() usage isn't exception safe.
See:
in the implementation of what() for an example.
Thanks, I've marked up the source code with you note and will make changes accordingly. --Beman

Beman Dawes wrote on 25/06/2006 15:00:42:
And any other comments are welcome!
Could the next release please have a bjam file please and also do an install. Thanks /ikh ________________________________________________________________________ This email has been scanned for all viruses by the MessageLabs SkyScan service.

<Iain.Hanson@trendcomms.com> wrote in message news:OF497234BE.E28B2520-ON8025719B.004F4B54-8025719B.004FCE83@trendcomms.com...
Beman Dawes wrote on 25/06/2006 15:00:42:
And any other comments are welcome!
Could the next release please have a bjam file please and also do an install.
I've added those to my TODO list. Thanks, --Beman

Beman Dawes wrote:
The filesystem proposal accepted by the LWG for TR2 also includes a <system_error> header with a bit of error reporting machinery. In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti...
The plan is to use these classes uniformly in TR2. Presumably we will want Boost implementations, and use them in other Boost libraries besides Boost.Filesystem.
[snip]
And any other comments are welcome!
--Beman
Hello Beman! The N1975 proposal has a section called "Important Design Decisions". It is a very good place to put a rational why you've chosen the error reporting strategy with system_error. currently, it has only one sentence "Because filesystem operations often encounter unexpected runtime errors, the library by default reports runtime errors via C++ exceptions, and ensures enough information is provided for meaningful error messages, including internationalized error messages." It is not enough to understand why you don't follow the way of other portable file system frameworks. For example, in java language there are IOException and its descendants like FileNotFoundException. It can be said that it follows the usual OO-exception handling mantra - define a hierarchy of exception classes and use them to report error conditions from your functions. Furthermore, it can be said that it is a way to abstract low-level system errors and provide only high level logical errors (or exceptions). It is clear from n1975 that you abandoned the OO approach and fall back to the C-style error reporting and handling. Yes there are exceptions, but no hierarchy. And exception handling is done by dispatching on a base of error codes but not on exception types. So, it is not really far from "C" I believe. One more point is that type safety is abandoned here too. I should add here that I agree with your decision, but I think that it shouldn't be made implicit, without a really good explanation and rational. The reason is simple: it changes the paradigm that is written in all OO and/or C++ books. Best regards, Oleg Abrosimov.

"Oleg Abrosimov" <beholder@gorodok.net> wrote in message news:44A409E6.9000600@gorodok.net...
Beman Dawes wrote:
The filesystem proposal accepted by the LWG for TR2 also includes a <system_error> header with a bit of error reporting machinery. In particular, class error_code to encapsulate error codes from the operating system and class system_error to be thrown by exceptions.
See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n1975.html#Diagnosti...
The plan is to use these classes uniformly in TR2. Presumably we will want Boost implementations, and use them in other Boost libraries besides Boost.Filesystem.
[snip]
And any other comments are welcome!
--Beman
Hello Beman!
The N1975 proposal has a section called "Important Design Decisions". It is a very good place to put a rational why you've chosen the error reporting strategy with system_error.
currently, it has only one sentence "Because filesystem operations often encounter unexpected runtime errors, the library by default reports runtime errors via C++ exceptions, and ensures enough information is provided for meaningful error messages, including internationalized error messages."
It is not enough to understand why you don't follow the way of other portable file system frameworks. For example, in java language there are IOException and its descendants like FileNotFoundException. It can be said that it follows the usual OO-exception handling mantra - define a hierarchy of exception classes and use them to report error conditions from your functions. Furthermore, it can be said that it is a way to abstract low-level system errors and provide only high level logical errors (or exceptions).
Yes, I'm well aware of that approach and was one of the participants in LWG at the time that approach was designed back in the early 1990's. A strength of such a high level approach is that it abstracts away a lot of differences between operating systems. But sometimes programmers need to deal with those lower level details. The set of error conditions that has to be dealt with is also open ended, and exception hierarchies where there is one exception for each error type have trouble dealing with that. There is also the issue of interface bloat - there are a very large number of potential errors, and having an exception type for each one of them become unwieldy.
It is clear from n1975 that you abandoned the OO approach and fall back to the C-style error reporting and handling. Yes there are exceptions, but no hierarchy. And exception handling is done by dispatching on a base of error codes but not on exception types. So, it is not really far from "C" I believe.
That was the intent. To provide a C++ interface that is higher level than the C interface, but not so high level that programmers can't get at the details.
One more point is that type safety is abandoned here too.
Not sure what you mean here. Could you give an example?
I should add here that I agree with your decision, but I think that it shouldn't be made implicit, without a really good explanation and rational. The reason is simple: it changes the paradigm that is written in all OO and/or C++ books.
The plan is to take the feedback from Boosters, improve the details of the interfaces, and present a proposal to the LWG refining the work that has already been accepted. I'll try to add more rationale to that document. Thanks, --Beman

Beman Dawes wrote:
"Oleg Abrosimov" <beholder@gorodok.net> wrote
Hello Beman!
The N1975 proposal has a section called "Important Design Decisions". It is a very good place to put a rational why you've chosen the error reporting strategy with system_error.
currently, it has only one sentence "Because filesystem operations often encounter unexpected runtime errors, the library by default reports runtime errors via C++ exceptions, and ensures enough information is provided for meaningful error messages, including internationalized error messages."
It is not enough to understand why you don't follow the way of other portable file system frameworks. For example, in java language there are IOException and its descendants like FileNotFoundException. It can be said that it follows the usual OO-exception handling mantra - define a hierarchy of exception classes and use them to report error conditions from your functions. Furthermore, it can be said that it is a way to abstract low-level system errors and provide only high level logical errors (or exceptions).
Yes, I'm well aware of that approach and was one of the participants in LWG at the time that approach was designed back in the early 1990's.
It seems that you are the right person to ask my questions. See below.
A strength of such a high level approach is that it abstracts away a lot of differences between operating systems. But sometimes programmers need to deal with those lower level details. The set of error conditions that has to be dealt with is also open ended, and exception hierarchies where there is one exception for each error type have trouble dealing with that. There is also the issue of interface bloat - there are a very large number of potential errors, and having an exception type for each one of them become unwieldy.
There is also one more issue on client side. If library provides many exception classes that can be thrown and user is interested in many of them ( he can not just use catch(lib_base_exception&) {...} trick ) then code becomes (1) _very_ bloated and (2) unmaintainable: void some_client_func() try { call_lib_with_many_exceptions(); } catch(exception1& e1) { do_response1(e1); } catch(exception2& e2) { do_response2(e2); } catch(exception3& e3) { do_response3(e3); } catch(exception4& e4) { do_response4(e4); } catch(exception5& e5) { do_response5(e5); } catch(exception6& e6) { do_response6(e6); } 1) code is bloated; 2) this exception handling code is duplicated many times in many of some_client_func's. It means that every time when exception handling code should be changed or exception7 handler should be added one needs to make corrections in many places. It is well known as a "maintenance hell". Actually, this bloating problem quickly arises in production code that tries to use exceptions. Even without libs that publish too many exception types. (std::string, const char*, CImageException, std::exception and catch all handlers are the real example from one of real-world projects I've worked on) In java language, for example, this problem is avoided because compiler enforces exception handling in the place where it can be thrown: int n = 0; try { n = Integer.parseInt("1"); } catch(NumberFormatException e) {} it can not be simply written as: int n = Integer.parseInt("1"); because in this case one must specify NumberFormatException in throws specification of enclosing function. As a consequence, it leads to "handle one exception at a time" principle. In C++ it can not be applied, because every statement can throw and compiler can't help you with it. As a consequence, one ends up with many exception handlers that bloats code as was shown above. Actually, there can be a workaround for the bloating/maintenance problem. I can imagine a higher-level function that gets a function to call and something like mpl map where keys are types of exceptions to handle and values are corresponding exception handlers. In this case my exception handling code becomes: void do_response1(exception1& e1); void do_response2(exception2& e2); void do_response3(exception3& e3); void do_response4(exception4& e4); void do_response5(exception5& e5); void do_response6(exception6& e6); typedef ... exception_handlers_map; void some_client_func() { call<exception_handlers_map>(&call_lib_with_many_exceptions); } Then when one needs to replace do_response5 handler with more sophisticated one (and only for one client_func) then he do apply some mpl algorithm for that and specify the result of this algorithm in call<...>(...) statement. This solution looks promising but in practice it would results in code like this: void some_client_func_impl(); // provides actual implementation that can throw many exceptions void some_client_func() { call<exception_handlers_map>(&some_client_func_impl); } May be not too bad, just one more trampoline function, but it looks like a hack that workarounds some deficiency in C++ exception handling mechanism. Can you please elaborate to improve my understanding of what this deficiency is and how it can be solved in the first place - in language itself? Or maybe I've just missed something in C++'s exception handling machinery?
It is clear from n1975 that you abandoned the OO approach and fall back to the C-style error reporting and handling. Yes there are exceptions, but no hierarchy. And exception handling is done by dispatching on a base of error codes but not on exception types. So, it is not really far from "C" I believe.
That was the intent. To provide a C++ interface that is higher level than the C interface, but not so high level that programmers can't get at the details.
consider the following code: // untested namespace boost { namespace win { class win_specific_error1; //... class win_specific_errorN; } namespace posix { class posix_specific_error1; //... class posix_specific_errorM; } } All these error classes are inherited from filesystem_error. Then client can obtain all information in a typesafe manner: try { call_filesystem_lib(); } catch(win_specific_error10& e) { ... } catch(win_specific_error20& e) { ... } Yes, it bloats both, library interface and client exception handling code, but: 1) library interface is written one time and used many times. For me it means that a huge amount of exception classes hidden in special namespaces is not an issue. It doesn't complicates simple usages when only abstract exceptions like filesystem_error are handled by client and provides a consistent and typesafe way for those who wants all the low level information. 2) bloating on the client side was discussed above and can be solved as was shown. (It needs further discussion though)
One more point is that type safety is abandoned here too.
Not sure what you mean here. Could you give an example?
try { call_filesystem_lib(); } catch (filesystem_error& e) { switch(e.code()) { case 1 : do_responce1(); break; case 2 : do_responce2(); break; default : assert(false); // unknown error occurred! } } can you see typesafety here? I can not. For me it is not different from: int err_code = call_filesystem_lib_from_C(); switch(err_code) { case 1 : do_responce1(); break; case 2 : do_responce2(); break; default : assert(false); // unknown error occurred! } hope you see my point now. To conclude: 1) there seems to be a real deficiency in exception handling mechanism that prevents library authors from providing many useful exception classes. This problem needs further investigation. in the meantime it can be workarounded with approach shown in this message (or better one). 2) filesystem error handling machinery reflects the problem mentioned and falls back to the run-time dispatching based on error code that is not type safe.
I should add here that I agree with your decision, but I think that it shouldn't be made implicit, without a really good explanation and rational. The reason is simple: it changes the paradigm that is written in all OO and/or C++ books.
The plan is to take the feedback from Boosters, improve the details of the interfaces, and present a proposal to the LWG refining the work that has already been accepted. I'll try to add more rationale to that document.
Thanks,
--Beman
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Finally, with a help of "Exception Visitor" thread I've understood the problem I've tried to spot in my previous message. In current C++ language exception handling is defined with well structured exception hierarchies in mind. In this case exception handling is simple: catch(base_exception& e) and be happy. The problem arises when one have to handle many unrelated exceptions that do not correspond to single hierarchy or even in a rare case where one needs to provide different responses for different exceptions in the same hierarchy. The first is common on subsystem boundaries like C++ - C or C++ - GUI event handlers. In the thread mentioned above two basic workarounds were described that helps to deal with a problem of many unrelated exception handling, but I believe that it can be elegantly solved on a language level. All we need is just allow the following: class A {}; class B { public : B(A const& a); }; void foo() try { throw A; } catch (B& b) { //caught } The idea behind it is simple: if A was thrown and B can be constructed from it then the catch(B& b) handler is called. With this extension one can do the following: typedef boost::variant< int, std::string > my_exception; class my_exception_visitor : public boost::static_visitor<> { public: void operator()(int & i) const { //... } void operator()(std::string & str) const { //... } }; void foo() try { if(...) throw 1; else throw std::string("Hello world!"); } catch (my_exception& e) { boost::apply_visitor( my_exception_visitor(), e ); } for filesystem it means that it can use uniform "exception handling" even without exceptions at all. versions of functions with error_code could be replaced with templates that can accept any visitor instead of error_code. It would be a high-level, type-safe, reusable and maintainable way to handle system-level errors. I'll post this idea to comp.std.c++ in a hope of it's adoption by community. But in the meantime my request to you as a filesystem and system_error developer is to use type-safe visitor approach instead of error_code. It means that you'll need to define a separate error type for each low-level error on each supported platform. Yes, it can be very annoying, but it should be done only one time. (it can be extended if the underling system error-set would be extended though) And maybe it can be automated with some text processing tools? Best, Oleg Abrosimov.
participants (7)
-
Beman Dawes
-
Christopher Kohlhoff
-
David Abrahams
-
Gennaro Prota
-
Iain.Hanson@trendcomms.com
-
Oleg Abrosimov
-
Pavel Vozenilek