|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 |
|
Messages: n/a
Hébergeur: |
Consider this program, which defines a template class who's template
parameter is the type of an exception that can be thrown by members of the class: === BEGIN EXAMPLE === #include <iostream> #include <string> using namespace std; void function () throw (string) { throw string("Oops"); } template <class EX> class A { public: typedef void (* FN) (); explicit A (FN f) : f_(f) { } void Call () throw (EX) { f_(); } private: FN f_; }; int main () { A<string> a(&function); try { a.Call(); } catch (const string &s) { cout << s << endl; } } === END EXAMPLE === The goal of the above is to allow the callback function to throw any exception it wants (not necessarily derived from std::exception, so simply specifying that is not an option), and still have the generated template code for A have the correct throw() specifier for A::Call(). I have a couple of questions: 1. Is having a correct exception specifier even necessary? It's always been one of those things that C++ has that compilers never seem to care about (compare to Java, for example, where it is strictly enforced). VC++ actually generates a compiler warning that throw() specifiers are ignored, GCC doesn't care, Borland's compiler is the only one I know of off the top of my head that actually produces diagnostics when throw() specifications are violated. If it really doesn't matter (and won't matter in C++0x either), then the above template stuff is completely unnecessary since I can just declare Call() with no exception specification at all and not have to deal with it. 2. If having the correct specification is necessary, is there some trick I can use to keep you from having to explicitly specify the exception type template parameter when declaring an A? Some way that I can make it implicit? In the above program, for example, function() is declared as throwing a string -- is there some way I can make A<> be aware of that so I can do "A a(function)" instead of "A<string> a(function)"? AFAIK function pointer types can't have throw() information in them so putting it in the A::FN type is not something I seem to be able to do. Hopefully this question makes sense; maybe I am thinking about it too much, but I never really feel comfortable when I throw an exception from a function that is declared with no exception specifier at all -- plus it's a useful self-documentation tool as well. Thanks, Jason |
|
|
|
#2 |
|
Messages: n/a
Hébergeur: |
On Jun 4, 7:58 pm, "jason.cipri...@gmail.com"
<jason.cipri...@gmail.com> wrote: > It's always > been one of those things that C++ has that compilers never seem to > care about (compare to Java, for example, where it is strictly > enforced). VC++ actually generates a compiler warning that throw() > specifiers are ignored, GCC doesn't care, Borland's compiler is the > only one I know of off the top of my head that actually produces > diagnostics when throw() specifications are violated. Also, Comeau doesn't care about things like this either: void function () throw (A) { throw NotA(); } |
|
|
|
#3 |
|
Messages: n/a
Hébergeur: |
jason.cipriani@gmail.com wrote:
> Consider this program, which defines a template class who's template > parameter is the type of an exception that can be thrown by members of > the class: > > === BEGIN EXAMPLE === > > #include <iostream> > #include <string> > using namespace std; > > void function () throw (string) { > throw string("Oops"); > } > > template <class EX> class A { > public: > typedef void (* FN) (); > explicit A (FN f) : f_(f) { } > void Call () throw (EX) { f_(); } > private: > FN f_; > }; > > int main () { > A<string> a(&function); > try { > a.Call(); > } catch (const string &s) { > cout << s << endl; > } > } > > === END EXAMPLE === > > The goal of the above is to allow the callback function to throw any > exception it wants (not necessarily derived from std::exception, so > simply specifying that is not an option), and still have the generated > template code for A have the correct throw() specifier for A::Call(). What if the callback function can throw objects of various types? > I have a couple of questions: > > 1. Is having a correct exception specifier even necessary? It's always > been one of those things that C++ has that compilers never seem to > care about (compare to Java, for example, where it is strictly > enforced). VC++ actually generates a compiler warning that throw() > specifiers are ignored, GCC doesn't care, Borland's compiler is the > only one I know of off the top of my head that actually produces > diagnostics when throw() specifications are violated. You seem to misunderstand throw specifications. They are enforced at run-time not at compile time. If an exception wants to unwind the stack past a function from which it is not allowed to escape according to the exception specification, then the function unexpected() is called. > If it really > doesn't matter (and won't matter in C++0x either), then the above > template stuff is completely unnecessary since I can just declare > Call() with no exception specification at all and not have to deal > with it. By and large, throw specifications can be considered useless with the notable exception of throw(). [snip] Best Kai-Uwe Bux |
|
|
|
#4 |
|
Messages: n/a
Hébergeur: |
On Jun 4, 8:22 pm, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
> You seem to misunderstand throw specifications. They are enforced at > run-time not at compile time. If an exception wants to unwind the stack > past a function from which it is not allowed to escape according to the > exception specification, then the function unexpected() is called. I did misunderstand; I am so used to compilers enforcing it in Java that I just kind of assumed C++ would handle them similarly. Fascinating! I had no idea that you could use set_unexpected() to change the behavior. In fact, long ago I had once thrown an exception from a function with a different throw() specification, and the program terminated. I had immediately assumed that it was because perhaps the compiler had performed some optimizations or something and some weird internal error occurred when I threw an exception it didn't expect to have to handle when compiling the code. I didn't even look into it any further, that event just made me paranoid and started me down the road of always using throw() specifications and sticking to them (which I guess isn't a bad thing but I did it for the wrong reasons). So, when I test this with GCC 4.1.2: class A { }; class B { }; void f1 () throw (A) { throw B(); } void f2 () { throw B(); } A call to f1() leads to unexpected() being called, as expected (heh). However, a call to f2() works just fine. It seems that not specifying throw(...) at all is the same as saying "this function can throw anything it wants". That's correct, right? It's not a GCC quirk? Now, in that case, I still have my original question although for different reasons. I'd like to be able to enforce strict exception handling at run-time, and so I'd need to specify the exception type that the "callback function" can throw in that template class. Right? Is there a better way to do that? If my goal is to enforce that kind of exception handling, and my situation is like what it was in my original post (example at end of this post for reference), is that the way to do it? > > If it really > > doesn't matter (and won't matter in C++0x either), then the above > > template stuff is completely unnecessary since I can just declare > > Call() with no exception specification at all and not have to deal > > with it. > > By and large, throw specifications can be considered useless with the > notable exception of throw(). Do you think that they are reliably useful for checking for developer errors at runtime, at least? Thanks, Jason > === BEGIN EXAMPLE === > #include <iostream> > #include <string> > using namespace std; > void function () throw (string) { > throw string("Oops"); > } > template <class EX> class A { > public: > typedef void (* FN) (); > explicit A (FN f) : f_(f) { } > void Call () throw (EX) { f_(); } > private: > FN f_; > }; > int main () { > A<string> a(&function); > try { > a.Call(); > } catch (const string &s) { > cout << s << endl; > } > } > === END EXAMPLE === > The goal of the above is to allow the callback function to throw any > exception it wants (not necessarily derived from std::exception, so > simply specifying that is not an option), and still have the generated > template code for A have the correct throw() specifier for A::Call(). |
|
|
|
#5 |
|
Messages: n/a
Hébergeur: |
On Jun 4, 9:02 pm, "jason.cipri...@gmail.com"
<jason.cipri...@gmail.com> wrote: > Now, in that case, I still have my original question although for > different reasons. I'd like to be able to enforce strict exception > handling at run-time, and so I'd need to specify the exception type > that the "callback function" can throw in that template class. Right? > Is there a better way to do that? If my goal is to enforce that kind > of exception handling, and my situation is like what it was in my > original post (example at end of this post for reference), is that the > way to do it? Or, do you think, in that situation, it's better to declare Call() with no throw specification at all, letting it throw anything, and just let the program abort if whoever is calling Call() doesn't handle the exception that the callback function throws? That seems like a simpler solution. Jason |
|
|
|
#6 |
|
Messages: n/a
Hébergeur: |
jason.cipriani@gmail.com wrote:
> On Jun 4, 9:02 pm, "jason.cipri...@gmail.com" > <jason.cipri...@gmail.com> wrote: >> Now, in that case, I still have my original question although for >> different reasons. I'd like to be able to enforce strict exception >> handling at run-time, and so I'd need to specify the exception type >> that the "callback function" can throw in that template class. Right? >> Is there a better way to do that? If my goal is to enforce that kind >> of exception handling, and my situation is like what it was in my >> original post (example at end of this post for reference), is that the >> way to do it? > > Or, do you think, in that situation, it's better to declare Call() > with no throw specification at all, letting it throw anything, and > just let the program abort if whoever is calling Call() doesn't handle > the exception that the callback function throws? > > That seems like a simpler solution. Yes. It is simpler. An exception specification is like an assert(). However, unlike an assert() it is usually not locally verifiable especially in templated code or code that calls functors because in that client supplied stuff might get executed and throw anything the client felt like throwing. E.g., look at something simple like template < typename InIter, typename OutIter, typename T > OutIter transform ( InIter from, InIter to, OutIter where, T t ) { while ( from != to ) { *where++ = t( *from++ ); } return ( *where ); } Any line in here might throw. Especially t( *from ) can throw anything and the assignment operator might try to copy huge data and run out of memory. A correct throw specification based upon the types is impossible (since what can be thrown might actually depend on the _value_ of t not just on the type T). It is much better to leave the decision on what can be thrown to the client and in turn let it be the clients responsibility to make sure every possible exception is handled correctly. Best Kai-Uwe Bux |
|
|
|
#7 |
|
Messages: n/a
Hébergeur: |
On Jun 5, 3:33 am, Kai-Uwe Bux <jkherci...@gmx.net> wrote:
> > Or, do you think, in that situation, it's better to declare Call() > > with no throw specification at all, letting it throw anything, and > > just let the program abort if whoever is calling Call() doesn't handle > > the exception that the callback function throws? > > > That seems like a simpler solution. > > Yes. It is simpler. That's the way I ended up doing it, rather than messing around with the throw() specifications. > An exception specification is like an assert(). However, unlike an assert() > it is usually not locally verifiable especially in templated code or code > that calls functors because in that client supplied stuff might get > executed and throw anything the client felt like throwing. This makes complete sense to me, and clears everything up. Thanks. > It is much better to leave the decision on what can be thrown to the client > and in turn let it be the clients responsibility to make sure every > possible exception is handled correctly. Thanks a lot for your reply, that answers all my questions. Jason |
|
![]() |
| Outils de la discussion | |
|
|