Open tcorbat opened 6 years ago
The example isn't a good use of a function try block. I think "don't use function try blocks" is a better guideline that applies more often.
I disagree with the proposal as written now because except.handle 13 says
If a return statement (9.6.3) appears in a handler of the function-try-block of a constructor, the program is ill-formed.
This contradicts your rule Explicitly return or throw at end of constructor’s/destructor’s function try block.
Oh dear, how could I miss [except.handle]/13. This makes the return part completely wrong indeed.
Suggesting to avoid function try blocks (at least for constructors/destructors) seems to be reasonable for me too.
Suggesting to avoid function try blocks (at least for constructors/destructors)
Avoid them everywhere.
Avoid them everywhere.
I was going to say "what about converting and rethrowing exceptions", but then I never had to do that myself.. So I just ran Debian Code Search for \)\s*try\s*:
(not perfect since it won't catch ones with line breaks) and it found 243 uses in 30 packages, Almost all of them were compiler/IDE/other C++ parser test suites or that regex latching on to something that isn't C++.
I only found three uses in the entire Debian:
valid = false;
in the catch clause.+1 for "Avoid them everywhere".
Although I recognise most functions shouldn't have function-try blocks, the suggestion to avoid it, is it because:
or
If (2), I suppose this would apply to putting try-catch on a for
-, while
- and if
-block as well. I do use try-blocks as for
-loop bodies sometimes, if they handle events that are allowed to fail independently, without preventing all subsequent events from being executed.
I have a few example of cases where function try-blocks are convenient. For instance (because they save an indentation level):
main()
function of a utility where any error propagated to that level should print an error and return a non-zero status code. example@cubbimew I suppose my code didn't show up in your code search.
Windows uses function try on ABI boundaries with the main reason being concision. I like @arvidn points and think the main() example is illustrative (and I have written that myself many times). if guidelines say never I need to learn more about what I'm getting wrong.
We will clarify the existing rules about function-try block for constructors/destructor.
@arvidn
If (2), I suppose this would apply to putting try-catch on a for-, while- and if-block as well.
No, not at all. A try-catch
block is a statement, so I've never met anybody who is surprised that you can use it as the body of a for
or while
loop. It's no different to a single statement without braces:
while (cond)
++i; /* a single statement
while (cond)
{ /* a block statement */; }
while (cond)
try { } catch (...) { };
But a function try block is unique syntax, allowing try
to appear outside a function body. You can't do that with anything else:
void func() while (cond) { }
void func(int i) ++i; { }
It's a completely false analogy to say that if people aren't familiar with function try blocks they aren't familiar with other perfectly normal uses of try-catch.
It's a completely false analogy to say that if people aren't familiar with function try blocks they aren't familiar with other perfectly normal uses of them.
It is my personal experience that it's common to learn about these uses of try-catch blocks together, and colleagues being surprised of both. But obviously I can't claim this is the case universally.
My impression is that function try blocks are little known because many, probably most, textbooks don't mention them.
I have a suggestion for an additional rule regarding function try blocks for constructors/destructors. As [except.handling]/14 [1] states that if control flow reaches the end of a handler of a constructor’s/destructor’s function try block, the handled exception is rethrown. To me (and I think for others it might be too) this case is surprising and therefore I suggest to always state this behavior explicitly (at least in the case mentioned), i.e. insert a
throw;
orreturn
statement at the end of all handlers of constructor’s/destructor’s function try block. I'm curious whether others consider this as an issue too.[1] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/n4741.pdf
Possible rule:
E.XX: Explicitly
throw
at end of constructor’s/destructor’s function try blockReason:
If controlflow reaches the end of a constructor's/destructor’s handler of a function try block the handled exception will be implicitly rethrown. The behavior is different in any other catch handler. Therefore, the desired behavior should be stated explicitly in this case.
Example, bad:
Example:
Enforcement:
Flag catch handlers of a constructor’s function try block without explicit
throw
statement. Flag catch handlers of a destructor’s function try block without explicitreturn
orthrow
statement.