exception, filesystem, system_error and consistency
I haven't used C++ for several years and it's only a few weeks since I've looked at boost for the first time. Thus it's likely that my understanding of things isn't quite up to scratch. In my first C++ project for years, I'm already using boost::filesystem and I'm using boost::system::system_error to wrap error codes returned by Linux API functions. For the latter purpose, I intend to use boost::exception for its ability to add diagnostic information to exceptions. At the top-level of my own code, I implement plain C interfaces and therefore need to convert all exceptions back to their original error codes. Poring over docs and headers, I noticed that neither boost::system::system_error nor boost::filesystem::filesystem_error are derived from boost::exception. Instead they implement an independent exception hierarchy. I found this disconcerting. For one thing, I have to catch and handle the respective exceptions separately. Far more importantly, though, I was expecting consistency among boost libraries and now see that this is not the case apparently. When there is more than one way to do something, this automatically raises the question which one is the right way. Regarding boost, I'm wondering whether all libraries are created equal or whether there is an undocumented gradation from "stable, recommended" to "experimental, beware". Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
On Fri, Jun 10, 2011 at 1:09 PM, Michael Schuerig
Poring over docs and headers, I noticed that neither boost::system::system_error nor boost::filesystem::filesystem_error are derived from boost::exception. Instead they implement an independent exception hierarchy.
I found this disconcerting. For one thing, I have to catch and handle the respective exceptions separately. Far more importantly, though, I was expecting consistency among boost libraries and now see that this is not the case apparently.
The reason for this discrepancy is that Boost Exception was added later than boost::filesystem and boost::system. With the introduction of Boost Exception, we now have a single base exception type for Boost libraries to use, but rather than modifying all existing Boost libraries, the boost::throw_exception function (which most Boost libraries have always been calling to throw exceptions) was modified so that the object it throws is guaranteed to derive from boost::exception. This is done at the point the throw_exception template is instantiated: if you pass it an object of type T which doesn't derive from boost::exception, it is wrapped in a type that derives both from T and from boost::exception (this mechanism is formally documented here: http://www.boost.org/doc/libs/release/libs/exception/doc/throw_exception.htm...) HTH, Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Saturday 11 June 2011, Emil Dotchevski wrote:
On Fri, Jun 10, 2011 at 1:09 PM, Michael Schuerig
wrote: Poring over docs and headers, I noticed that neither boost::system::system_error nor boost::filesystem::filesystem_error are derived from boost::exception. Instead they implement an independent exception hierarchy.
I found this disconcerting. For one thing, I have to catch and handle the respective exceptions separately. Far more importantly, though, I was expecting consistency among boost libraries and now see that this is not the case apparently.
The reason for this discrepancy is that Boost Exception was added later than boost::filesystem and boost::system.
With the introduction of Boost Exception, we now have a single base exception type for Boost libraries to use, but rather than modifying all existing Boost libraries, the boost::throw_exception function (which most Boost libraries have always been calling to throw exceptions) was modified so that the object it throws is guaranteed to derive from boost::exception. This is done at the point the throw_exception template is instantiated: if you pass it an object of type T which doesn't derive from boost::exception, it is wrapped in a type that derives both from T and from boost::exception (this mechanism is formally documented here: http://www.boost.org/doc/libs/release/libs/exception/doc/throw_except ion.html)
Yes, I had seen that in the docs, but there's also this bit in filesystem/v3/config.hpp: // Exceptions were originally thrown via boost::throw_exception(). // As throw_exception() became more complex, it caused user error reporting // to be harder to interpret, since the exception reported became much more complex. // The immediate fix was to throw directly, wrapped in a macro to make any later change // easier. #define BOOST_FILESYSTEM_THROW(EX) throw EX What am I supposed to make of this? The wording of the comment suggests that the implementers of boost::filesystem have tried to use boost::exception, but as it has proved unwieldy, they are not using it anymore. Now, for me, still being new to boost, that sounds like a strong advice to "do as we do". I don't understand all the subtle issues involved, so I may better keep away from boost::exception, too. I certainly won't just redefine BOOST_FILESYSTEM_THROW to BOOST_THROW_EXCEPTION. I may be overdramatising just a tiny bit, but the issue stands that these real or perceived inconsistencies have a strong impact on my impression of boost. I was approaching boost with the expectation of a set of well-coordinated libraries, coming with the recommendation "if you do it at all, do it like this". For me, this picture has got cracks. Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
Michael Schuerig wrote:
On Saturday 11 June 2011, Emil Dotchevski wrote:
On Fri, Jun 10, 2011 at 1:09 PM, Michael Schuerig
With the introduction of Boost Exception, we now have a single base exception type for Boost libraries to use, but rather than modifying all existing Boost libraries, the boost::throw_exception function (which most Boost libraries have always been calling to throw exceptions) was modified so that the object it throws is guaranteed to derive from boost::exception. This is done at the point the throw_exception template is instantiated: if you pass it an object of type T which doesn't derive from boost::exception, it is wrapped in a type that derives both from T and from boost::exception (this mechanism is formally documented here: http://www.boost.org/doc/libs/release/libs/exception/doc/throw_except ion.html)
Yes, I had seen that in the docs, but there's also this bit in filesystem/v3/config.hpp:
// Exceptions were originally thrown via boost::throw_exception(). // As throw_exception() became more complex, it caused user error reporting // to be harder to interpret, since the exception reported became much more complex. // The immediate fix was to throw directly, wrapped in a macro to make any later change // easier.
#define BOOST_FILESYSTEM_THROW(EX) throw EX
What am I supposed to make of this? The wording of the comment suggests that the implementers of boost::filesystem have tried to use boost::exception, but as it has proved unwieldy, they are not using it anymore.
Now, for me, still being new to boost, that sounds like a strong advice to "do as we do". I don't understand all the subtle issues involved, so I may better keep away from boost::exception, too. I certainly won't just redefine BOOST_FILESYSTEM_THROW to BOOST_THROW_EXCEPTION.
I may be overdramatising just a tiny bit, but the issue stands that these real or perceived inconsistencies have a strong impact on my impression of boost. I was approaching boost with the expectation of a set of well-coordinated libraries, coming with the recommendation "if you do it at all, do it like this". For me, this picture has got cracks.
You're not overdramatizing at all. In fact, it's just the opposite - you're UNDERdramatizing. The serialization library used the original boost exception functionality. This original functionality was such that it handled the case where the compiler didn't support (or the environment didn't want to support) normal C++ exceptions. This is common in things like embedded systems. All of a sudden, the symantics of the boost excpetion changed. This made available new functionality which no current libraries used and added many lines of header source to every module which used it. It also broke any libraries built without RTTI - another valid case. I complained about this at the time but I couldn't get any traction. I would have had no objection if the new exception had been an option - but it's invaded everything - I just used the thread library, and had a problem because of it. Changing the functionality of an existing function is a bad idea. I just can't understand why not everyone can see that. Robert Ramey
On Sat, Jun 11, 2011 at 4:00 PM, Robert Ramey
You're not overdramatizing at all. In fact, it's just the opposite - you're UNDERdramatizing. The serialization library used the original boost exception functionality. This original functionality was such that it handled the case where the compiler didn't support (or the environment didn't want to support) normal C++ exceptions. This is common in things like embedded systems. All of a sudden, the symantics of the boost excpetion changed.
The change doesn't break anything that wasn't already broken under the old semantics. In fact the change uncovered several bugs in Boost libraries not meeting the original requirements of boost::throw_exception.
This made available new functionality which no current libraries used
You're missing the point. Even if a Boost library, say Serialization, doesn't use the functionality of Boost Exception, the users of Boost Serialization could still use it. Your argument boils down to "I don't care if anyone needs to transport Serialization exceptions to another thread. Tough!"
and added many lines of header source to every module which used it.
About 400 lines. That code enables (does not implement) the functionality of Boost Exception. There is no way for anyone to make use of Boost Exception without that code.
It also broke any libraries built without RTTI - another valid case.
That was fixed long ago.
I complained about this at the time but I couldn't get any traction. I would have had no objection if the new exception had been an option - but it's invaded everything - I just used the thread library, and had a problem because of it.
Please do report problems you have. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
"Emil Dotchevski"
You're not overdramatizing at all. In fact, it's just the opposite - you're UNDERdramatizing. The serialization library used the original boost exception functionality. This original functionality was such that it handled the case where the compiler didn't support (or the environment didn't want to support) normal C++ exceptions. This is common in things like embedded systems. All of a sudden, the symantics of the boost excpetion changed.
The change doesn't break anything that wasn't already broken under the old semantics. In fact the change uncovered several bugs in Boost libraries not meeting the original requirements of boost::throw_exception.
This made available new functionality which no current libraries used
You're missing the point. Even if a Boost library, say Serialization, doesn't use the functionality of Boost Exception, the users of Boost Serialization could still use it. Your argument boils down to "I don't care if anyone needs to transport Serialization exceptions to another thread. Tough!"
and added many lines of header source to every module which used it.
About 400 lines. That code enables (does not implement) the functionality of Boost Exception. There is no way for anyone to make use of Boost Exception without that code.
It also broke any libraries built without RTTI - another valid case.
That was fixed long ago.
I complained about this at the time but I couldn't get any traction. I would have had no objection if the new exception had been an option - but it's invaded everything - I just used the thread library, and had a problem because of it.
Please do report problems you have. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
Emil Dotchevski wrote:
On Sat, Jun 11, 2011 at 4:00 PM, Robert Ramey
wrote:
The change doesn't break anything that wasn't already broken under the old semantics.
Hmmm - I remember it breaking serialization which as far as I know wasn't broken at the time.
This made available new functionality which no current libraries used
You're missing the point. Even if a Boost library, say Serialization, doesn't use the functionality of Boost Exception, the users of Boost Serialization could still use it.
Ahhh so now every library has to include something it doesn't use just because some other library might use it? So an application which wants to use it's own exception handling setup can't use any boost library?
Your argument boils down to "I don't care if anyone needs to transport Serialization exceptions to another thread. Tough!"
When writes a multithreaded ap, there is no expectation that exceptions will be passed from one thread to another without some sort of special treatment. This is doable in an obvious way. I can see how some sort of library might be helpful here. BUT there's no excuse for changing the behavior of a default exception mechanism to handle this case. It creates unnecessary "hidden" coupling. I'm guessing this is why a number of libraries have been changed not to include this.
and added many lines of header source to every module which used it.
About 400 lines. That code enables (does not implement) the functionality of Boost Exception. There is no way for anyone to make use of Boost Exception without that code.
not everyone (anyone?) makes use of Boost.Exception. But we all have to include another library with unexpected/unanticipated behavior
It also broke any libraries built without RTTI - another valid case.
That was fixed long ago.
Good to know.
I complained about this at the time but I couldn't get any traction. I would have had no objection if the new exception had been an option - but it's invaded everything - I just used the thread library, and had a problem because of it.
Please do report problems you have.
lol - I did this when it broke my debugging setup. The response I got seemed to suggest I was doing something I shouldn't be doing -- even though trapping when exceptions are thrown is perfectly legitimate. The idea that one is going to significantly change the functionality of a particular component already in use by a number of other libraries is really a bad idea and creates all sorts of uncertainty and surprises. Developers shouldn't have new behavior inserted into their code unless the specifically want to include it. I'm still at a loss to understand how this got through the review process. I admit I can't follow every review with an eye to possibility that just being included in boost is going to break my code or change it in a subtle and surprising way. But I shouldn't have to do this. Robert Ramey
Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Sun, Jun 12, 2011 at 12:16 AM, Robert Ramey
You're missing the point. Even if a Boost library, say Serialization, doesn't use the functionality of Boost Exception, the users of Boost Serialization could still use it.
Ahhh so now every library has to include something it doesn't use just because some other library might use it?
No, not some other library. We're talking about a *user* of Boost Serialization wanting to transport to another thread an exception Boost Serialization throws. Or a *user* of Boost Serialization wanting to record their own information in exceptions Boost Serialization throws.
Your argument boils down to "I don't care if anyone needs to transport Serialization exceptions to another thread. Tough!"
When writes a multithreaded ap, there is no expectation that exceptions will be passed from one thread to another without some sort of special treatment. This is doable in an obvious way. I can see how some sort of library might be helpful here.
To transport the exception, you need to copy it. In many cases or in generic contexts, copying an (exception) object isn't possible, nevermind obvious. Note that often multi-threaded systems execute arbitrary or user-defined functions which may throw any exception.
I'm guessing this is why a number of libraries have been changed not to include this.
I am not concerned about libraries not using boost::throw_exception, my only concern is that such a choice is made on behalf of users of those libraries.
and added many lines of header source to every module which used it.
About 400 lines. That code enables (does not implement) the functionality of Boost Exception. There is no way for anyone to make use of Boost Exception without that code.
not everyone (anyone?) makes use of Boost.Exception. But we all have to include another library with unexpected/unanticipated behavior
In your mind, how does Boost Exception interfere with the usual semantics of exception handling?
I complained about this at the time but I couldn't get any traction. I would have had no objection if the new exception had been an option - but it's invaded everything - I just used the thread library, and had a problem because of it.
Please do report problems you have.
lol - I did this when it broke my debugging setup. The response I got seemed to suggest I was doing something I shouldn't be doing -- even though trapping when exceptions are thrown is perfectly legitimate.
Even though it wasn't a bug, this problem was fixed. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Sunday 12 June 2011, Emil Dotchevski wrote:
I'm guessing this is why a number of libraries have been changed not to include this.
I am not concerned about libraries not using boost::throw_exception, my only concern is that such a choice is made on behalf of users of those libraries.
Well, what *are* users of those libraries supposed to do, then? From the outside, purely as a user, it looks to me like the boost authors haven't agreed about this issue (and presumably other) and I have to sort it out for myself. This is the same as in the normal case of using libs from several sources. It is just not what I was expecting. The Boost Library Reuse policy, http://www.boost.org/development/reuse.html , is rather permissive, it basically justs says what not to do (rely on non-std or non-boost libs), but only gives weak positive recommendations. What I *was* expecting is a policy that mandates higher-level libs use the services of lower-level ones if at all applicable. In other words, if libs don't play nicely together, at least one of them has to be adapted. Any squabbles ought to be resolved in released versions of boost. Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
On Sun, Jun 12, 2011 at 4:39 AM, Michael Schuerig
On Sunday 12 June 2011, Emil Dotchevski wrote:
I'm guessing this is why a number of libraries have been changed not to include this.
I am not concerned about libraries not using boost::throw_exception, my only concern is that such a choice is made on behalf of users of those libraries.
Well, what *are* users of those libraries supposed to do, then?
Provide feedback on this mailing list and request the features and fixes they need. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode
On Sunday 12 June 2011, Emil Dotchevski wrote:
On Sun, Jun 12, 2011 at 4:39 AM, Michael Schuerig
wrote: On Sunday 12 June 2011, Emil Dotchevski wrote:
I'm guessing this is why a number of libraries have been changed not to include this.
I am not concerned about libraries not using boost::throw_exception, my only concern is that such a choice is made on behalf of users of those libraries.
Well, what *are* users of those libraries supposed to do, then?
Provide feedback on this mailing list and request the features and fixes they need.
Dear boost developers, please advise on how to use (boost) exceptions to propagate errors originating from (Unix) system calls and how to handle these exceptions as well as those emerging from boost::filesystem in a suitable way to pass them on as error codes. Michael -- Michael Schuerig mailto:michael@schuerig.de http://www.schuerig.de/michael/
participants (4)
-
Emil Dotchevski
-
Emil Dotchevski
-
Michael Schuerig
-
Robert Ramey