
Hello, As a result of today's discussion, I added a function get_exception_info to the interface of the proposed Boost Exception library: template <class T> exception_info * get_exception_info( T & ); It can be used in a catch( T & ) block, to check if the exception object has a sub-object of class exception_info. I updated the documentation, source code and tests accordingly, all available at: http://www.revergestudios.com/exception/exception.htm Click "download" to get the code and the tests, complete with boost build jamfiles. Thanks, Emil

On 7/6/06, Emil Dotchevski <emildotchevski@hotmail.com> wrote:
template <class T> exception_info * get_exception_info( T & );
It can be used in a catch( T & ) block, to check if the exception object has a sub-object of class exception_info.
Why return a pointer? How about considering the use of a reference instead? Perhaps: template < typename T > exception_info & get_exception_info ( T & ); That way, the return can only be accessed using an exception_info reference. Consider this example (which is considerably cleaner to read, IMO) : try { throw failed<read_error>() << wrap_string<tag_file_name>("example.txt"); } catch (read_error & e) { exception_info info = get_exception_info(e); if (info) { // deal with the info... } // deal with the exception... }; This also avoids the possibility of mis-use of pointers, because client code can still do a cast of the pointer to refer to something else -- which although it is hackish, is something you'd want to avoid. It also makes the following possible: std::cout << get_string<tag_file_name>(get_exception_info(e)) << std::endl; Which is a little long for my taste, but readable nonetheless. HTH -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/7/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On 7/6/06, Emil Dotchevski <emildotchevski@hotmail.com> wrote:
template <class T> exception_info * get_exception_info( T & );
It can be used in a catch( T & ) block, to check if the exception object has a sub-object of class exception_info.
Why return a pointer? How about considering the use of a reference instead? Perhaps:
I prefer the pointer, that way one can define the variable inside the if if(exception_info* p = get_exception_info(e)) { }
template < typename T > exception_info & get_exception_info ( T & );
That way, the return can only be accessed using an exception_info reference. Consider this example (which is considerably cleaner to read, IMO) :
try { throw failed<read_error>() << wrap_string<tag_file_name>("example.txt"); } catch (read_error & e) { exception_info info = get_exception_info(e); if (info) { // deal with the info... }
// deal with the exception... };
IMO, the pointer is cleaner.
This also avoids the possibility of mis-use of pointers, because client code can still do a cast of the pointer to refer to something else -- which although it is hackish, is something you'd want to avoid. It also makes the following possible:
casts can be done with references just a-like.
std::cout << get_string<tag_file_name>(get_exception_info(e)) << std::endl;
Which is a little long for my taste, but readable nonetheless.
HTH
-- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com
-- Felipe Magno de Almeida

On 7/8/06, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
I prefer the pointer, that way one can define the variable inside the if
if(exception_info* p = get_exception_info(e)) {
}
I don't see the benefit in this since if exception_info is defined in the following sense: struct exception_info { exception_info() : is_valid(false) {}; exception_info(const exception_info & other) : is_valid(other.is_valid) {}; bool is_valid; ... operator bool () { // return true if it's a copy of a real // exception, false if it's uninitialized... return is_valid; } ... }; Then something like this will be possible: if (get_exception_info(e)) { std::cout << get_string<tag_file_name>(get_exception_info(e)) << std::endl; }
IMO, the pointer is cleaner.
Since a pointer is basically a primitive type which you can manipulate like an integral type, it opens up a can of worms you don't want to deal with -- and will only make the code a lot easier to get wrong. Since you can manipulate pointers like int's, the following operations will be valid: exception_info * x = get_exception_info(e); x++; ++x; But if you're using references, you give a cleaner and more definite handle to your data: exception_info & info (get_exception_info(e)); info ++; // won't work, unless operator++ is defined for exception_info HTH -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/7/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
But if you're using references, you give a cleaner and more definite handle to your data:
exception_info & info (get_exception_info(e)); info ++; // won't work, unless operator++ is defined for exception_info
If you go with references, what would happen in this situation: try { //Some client code. throw some_error(); //Note that this exception does not have exception_info associated with it. } catch(some_error& e) { exception_info& info(get_exception_info(e)); //The dynamic cast in get_exception_info returns a null pointer. } Jeremy

On 7/8/06, Jeremy Day <jeremy.day@gmail.com> wrote:
If you go with references, what would happen in this situation:
<snipped code> It all really boils down to the implementation of get_exception_info(T &) and exception_info. Consider that let's say exception_info is defined this way (for simplicity's sake): [untested] class default_exception; class exception_info : boost::noncopyable { private: bool is_valid; protected: exception_info() : is_valid(true) { }; void set_valid(bool b) { is_valid = b ; }; public: static default_exception _default; operator bool() { return is_valid; }; // ... other operations ... }; class default_exception : public exception_info { public: default_exception () { set_valid(false); } }; template < typename T > exception_info & get_exception_info (T & e) const { exception_info * p = dynamic_cast<exception_info>(&e); return (p == NULL) ? exception_info::_default : *p; }; So when you always get a reference to an object of type exception_info or derivatives, you can specialize the null object case -- and give the client code a consistent handle to the exception. It might count as too much voodoo to get something to return a reference, but the benefits outweight the costs IMO. It just makes the interface less "fool proof" and makes it harder to "shoot yourself in the foot" when using a library of this utility. HTH -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/7/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
So when you always get a reference to an object of type exception_info
Yes, always getting references to exception_info would be great. If that's an issue you can just catch exception_info&, instead of some other exception. However, I think that it is the author's (Emil? I forget now.) intention that the exception library could be dropped into current code with no difference. Thus if you currently catch some_exception you could still catch it, and then test (via the dynamic_cast, which is now hidden behind get_exception_info) for whether exception_info has been added to it. It might count as too much voodoo to get something to return a
reference, but the benefits outweight the costs IMO. It just makes the interface less "fool proof" and makes it harder to "shoot yourself in the foot" when using a library of this utility.
I'm all for doing whatever possible to keep library users from inadvertantly doing stupid things, but I personally feel that in this case the benefits of using a pointer (being able to catch old exceptions and test for the optional exception_info) outweigh the dangers of users potentially doing something ridiculous with the exception_info pointer. Jeremy

I'm all for doing whatever possible to keep library users from inadvertantly doing stupid things, but I personally feel that in this case the benefits of using a pointer (being able to catch old exceptions and test for the optional exception_info) outweigh the dangers of users potentially doing something ridiculous with the exception_info pointer.
You are exactly right. But let me clarify, you can catch an exception with no exception_info not only because it was thrown by "old" code, but also because it was thrown by "3rd-party" code which doesn't know about the exception library. The exception library is not intended to change the way people handle exceptions. You'd still be designing exception class hierarchies, and catching references to those types, because that's how C++ expects you to dispatch between different types of errors. The most important design goals of the exception library are: 1) To free your exception classes from having to carry values, and 2) To allow values to be added to an exception that was already thrown, regardless of its type, by catching exception_info & and re-throwing. --Emil

On 7/8/06, Jeremy Day <jeremy.day@gmail.com> wrote:
I'm all for doing whatever possible to keep library users from inadvertantly doing stupid things, but I personally feel that in this case the benefits of using a pointer (being able to catch old exceptions and test for the optional exception_info) outweigh the dangers of users potentially doing something ridiculous with the exception_info pointer.
With my example implementation of exception_info and get_exception_info which returns (always) a reference to exception_info, you achieve the same without having the user expect to retrieve a pointer -- with which you can get wrong a lot of times, compared to using references. So you get the best of both worlds. ;) -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/7/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
On 7/8/06, Felipe Magno de Almeida <felipe.m.almeida@gmail.com> wrote:
I prefer the pointer, that way one can define the variable inside the if
if(exception_info* p = get_exception_info(e)) {
}
I don't see the benefit in this since if exception_info is defined in the following sense:
struct exception_info { exception_info() : is_valid(false) {}; exception_info(const exception_info & other) : is_valid(other.is_valid) {}; bool is_valid; ... operator bool () { // return true if it's a copy of a real // exception, false if it's uninitialized... return is_valid; } ... };
Is it? I thought an expcetion_info was always valid and as the dynamic_cast works, it would return a null pointer in case there wasnt any object. Besides, how are you always returning a reference? How to take care of exception_info's lifetime? You're not easing client's lifes. If you want to receive a reference, you would have to return a temporary and hold it by that reference (it is possible because the temporary's life would be prolonged). But that doesnt buy anything since all you would be doing is making the learning curve steeper for the library all to prohibit pointer aritmetic. Which, IMO, is a little hard to see anybody with some sense doing in this case.
Then something like this will be possible:
if (get_exception_info(e)) { std::cout << get_string<tag_file_name>(get_exception_info(e)) << std::endl; }
You would have to pay the dynamic_cast for each call. Besides, repeating get_exception_info, IMO, only degrades readability
IMO, the pointer is cleaner.
Since a pointer is basically a primitive type which you can manipulate like an integral type, it opens up a can of worms you don't want to deal with -- and will only make the code a lot easier to get wrong. Since you can manipulate pointers like int's, the following operations will be valid:
exception_info * x = get_exception_info(e); x++; ++x;
But if you're using references, you give a cleaner and more definite handle to your data:
The idea here is use the pointer to be used in a conditional. Just like when someone uses dynamic_casts. if(A *p = dynamic_cast<A*>(obj)) { } else if(B* p = dynamic_cast<B*>(obj)) { } and so on...
exception_info & info (get_exception_info(e)); info ++; // won't work, unless operator++ is defined for exception_info
Which is too much trouble only for prohibiting pointer aritmetics.
HTH
-- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com
best regards, -- Felipe Magno de Almeida

On 7/8/06, Emil Dotchevski <emildotchevski@hotmail.com> wrote:
exception_info * x = get_exception_info(e); x++; ++x;
Let's assume get_exception_info returns a reference.
How do you prevent this code:
exception_info * x = &get_exception_info(e); x++; ++x;
You don't avoid it, but when you see this code, you're doing a whole lot more to do something stupid -- that's why I'm really proposing and would feel better returning references.
Pointers are part of the C++ language, like it or not. :)
Yeah, as much as I'd like to get away from it, it's the sad reality. ;) -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/7/06 5:31 PM, "Dean Michael Berris" <mikhailberis@gmail.com> wrote:
Since a pointer is basically a primitive type which you can manipulate like an integral type, it opens up a can of worms you don't want to deal with -- and will only make the code a lot easier to get wrong. Since you can manipulate pointers like int's, the following operations will be valid:
exception_info * x = get_exception_info(e); x++; ++x;
But if you're using references, you give a cleaner and more definite handle to your data:
exception_info & info (get_exception_info(e)); info ++; // won't work, unless operator++ is defined for exception_info
As another poster said, you can take the address of "info" and then repeat the same stupid pointer tricks. The programmer's advice, "guard against Murphy, not Machiavelli," applies here. Your pointer flaw isn't because of this library's API, it's a general problem in C++. That's because all pointers define the ++ and -- operators for use with array segments, ignoring the fact that a pointer can be indistinctively used for single objects. In other words: "if you see this code, fire the programmer". -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

On 7/9/06, Daryle Walker <darylew@hotmail.com> wrote:
exception_info & info (get_exception_info(e)); info ++; // won't work, unless operator++ is defined for exception_info
As another poster said, you can take the address of "info" and then repeat the same stupid pointer tricks. The programmer's advice, "guard against Murphy, not Machiavelli," applies here. Your pointer flaw isn't because of this library's API, it's a general problem in C++. That's because all pointers define the ++ and -- operators for use with array segments, ignoring the fact that a pointer can be indistinctively used for single objects. In other words: "if you see this code, fire the programmer".
That's why I would like to use references -- because if the user of the library _still_ got the address, then he _had_ to get the address and put it in a pointer to do something potentially stupid with it. That's just preventing the regular user from making a mistake by side-stepping pointers in the API itself. And I agree -- if the programmer _still_ did the stupid &get_exception_info() and did a ++ on the pointer, then maybe that programmer should get fired. ;) -- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com

On 7/10/06, Dean Michael Berris <mikhailberis@gmail.com> wrote:
[snipped]
And I agree -- if the programmer _still_ did the stupid &get_exception_info() and did a ++ on the pointer, then maybe that programmer should get fired. ;)
Maybe? :P
-- Dean Michael C. Berris C/C++ Software Architect Orange and Bronze Software Labs http://3w-agility.blogspot.com/ http://cplusplus-soup.blogspot.com/ Mobile: +639287291459 Email: dean [at] orangeandbronze [dot] com
-- Felipe Magno de Almeida

Why return a pointer? How about considering the use of a reference instead?
If you catch( foo & ), it is not guaranteed that it has an exception_info sub-object. For one thing, you may be converting existing code to the exception library, and there could be some throw foo() statements left around. But also, consider catching one of the standard exception types, for example std::length_error. It could have been thrown by the standard library and in that case it would not have exception_info sub-object; or, some other code could throw failed<std::length_error>(), and then there would be an exception_info sub-object. So, get_exception_info returns a pointer because it is intended to be used like this: catch( my_error & x ) { if( exception_info * xi = get_exception_info(x) ) { //use exception_info here } else { //handle the case of not having exception_info. } } --Emil
participants (5)
-
Daryle Walker
-
Dean Michael Berris
-
Emil Dotchevski
-
Felipe Magno de Almeida
-
Jeremy Day