Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

try...__finally problem

9 views
Skip to first unread message

Drew MacDonald

unread,
Sep 2, 2004, 5:23:38 PM9/2/04
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I am having a bit of a problem understanding how the try -
__finally blocks work. My understanding was (based on the
help files) that "the __finally keyword specifies actions
that should be taken regardless of how the flow within the
preceding try exits."

I came up with this simple test (since a more complex one
in my actual program failed) and it doesn't work the way I
expect it to.

void __fastcall TForm1::btn1Click(TObject *Sender)
{
~ TForm2 *t;

~ try
~ {
~ try
~ {
~ t = new TForm2(NULL);
~ t->ShowModal();
~ throw;
~ }
~ catch(...)
~ {
~ return;
~ }
~ }
~ __finally
~ {
~ delete(t);
~ MessageDlg("I got here!", mtConfirmation,
~ TMsgDlgButtons() << mbOK, 0);
~ }
}

TForm2 is a form with a button on it. The OnClick
code for the button is simply Close(). TForm1 is
a form with a button (btn1) on it. There is no other
code to this project other than what is automatically
generated by Builder. I am using Builder 5 Pro with the
update patch applied.

What I expect to have happen is that the TForm2, t,
will be displayed. I will click the close button then
t will disappear. An exception will be thrown but it
will be caught by the catch(...) block. Finally I should
get a message dialog saying "I got here!".

In actual fact, t is displayed, I click the button, the
form closes, then I get an error dialog saying "Abnormal
Program Termination" and the program dies.

What am I missing here? Shouldn't the catch(...) block
pick up the exception? Shouldn't the __finally block be
executing no matter what? I'd like to get this sorted
out as it would allow me to write a lot simpler code in
a few cases.

Thanks in advance.
Drew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBN49Z+cCIvS8c+5MRAtCDAJwOg6gNM+CNQrAwFoMxwOQFblbx0gCdFrHj
pnwS0kQXGjYjYTLErZ7UzL0=
=Q2bX
-----END PGP SIGNATURE-----

Chris Uzdavinis

unread,
Sep 2, 2004, 5:52:35 PM9/2/04
to
Drew MacDonald <drew_...@hotmail.com> writes:

> void __fastcall TForm1::btn1Click(TObject *Sender)
> {
> ~ TForm2 *t;
>
> ~ try
> ~ {
> ~ try
> ~ {
> ~ t = new TForm2(NULL);
> ~ t->ShowModal();
> ~ throw;

^^^^^^
Your problem is right here. A "throw" expression without an object
means it's supposed to re-throw the existing exception that is
currently being handled. When you re-throw in a context where there
is no current exception, your program will abort.

Change the above throw to something like:

throw Exception("some error message");

(Exception is a base class form which other exceptions can be derived.
It's a VCL object, while in normal C++ you can throw just about
anything, integers, objects, function pointers, booleans,
enums... just as long as it's a value in the program, it can be
thrown. But in this example, Exception("msg") instantiates an
Exception object with a given message, and that's what gets thrown.)

> In actual fact, t is displayed, I click the button, the
> form closes, then I get an error dialog saying "Abnormal
> Program Termination" and the program dies.
>
> What am I missing here? Shouldn't the catch(...) block
> pick up the exception? Shouldn't the __finally block be
> executing no matter what? I'd like to get this sorted
> out as it would allow me to write a lot simpler code in
> a few cases.

Keep in mind that a try-finally is non-standard, and there are more
efficient and less cumbersome ways to get the same behavior.
(Destructors of local variables can delete objects, etc.)

--
Chris (TeamB);

Drew MacDonald

unread,
Sep 2, 2004, 6:32:26 PM9/2/04
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Chris Uzdavinis (TeamB) wrote:

|>~ try
|>~ {
|>~ t = new TForm2(NULL);
|>~ t->ShowModal();
|>~ throw;
|
| ^^^^^^
| Your problem is right here. A "throw" expression without an object
| means it's supposed to re-throw the existing exception that is
| currently being handled. When you re-throw in a context where there
| is no current exception, your program will abort.

Thanks. I didn't realize that.

| Change the above throw to something like:
|
| throw Exception("some error message");

I tried that. I no longer get the "Abnormal Program Termination"
message, however I am not seeing the message dialog that is supposed
to be displayed in my finally block.

| Keep in mind that a try-finally is non-standard, and there are more
| efficient and less cumbersome ways to get the same behavior.
| (Destructors of local variables can delete objects, etc.)

I realize its non-standard, but I've got a situation where I have
15 exit points from a function (its old code I'm trying to maintain
so I don't want to mess with it too much) and putting cleanup code
at each point is ugly at best, difficult to maintain at worst. I
figured I could use the __finally block to do the cleanup no matter
at which point the function exited.

New code which still doesn't quite do what I want:

void __fastcall TForm1::btn1Click(TObject *Sender)
{
~ TForm2 *t;

~ try
~ {
~ try
~ {
~ t = new TForm2(NULL);
~ t->ShowModal();

~ throw Exception("Some kind of exception");


~ }
~ catch(...)
~ {
~ return;
~ }
~ }
~ __finally
~ {
~ delete(t);
~ MessageDlg("I got here!", mtConfirmation,
~ TMsgDlgButtons() << mbOK, 0);
~ }
}

Drew.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBN595+cCIvS8c+5MRAoDuAKCLFjD23mfpcH/EcF9s6LH6sPb+ygCfRkhr
hALr8dpeZ0e+Utoxkizzwow=
=0uXM
-----END PGP SIGNATURE-----

Remy Lebeau (TeamB)

unread,
Sep 2, 2004, 7:00:51 PM9/2/04
to

"Drew MacDonald" <drew_...@hotmail.com> wrote in message
news:2pplbrF...@uni-berlin.de...

> I am not seeing the message dialog that is supposed
> to be displayed in my finally block.

Try not calling 'return' from inside a try...finally block. It don't think
it works correctly in C++. Use this code instead:

void __fastcall TForm1::btn1Click(TObject *Sender)
{

TForm2 *t = NULL;

try
{
try
{
t = new TForm2(NULL);
t->ShowModal();


throw Exception("Some kind of exception");
}

__finally
{
delete t;
}
}
catch(const Exception &)
{
MessageDlg("I got here!", mtConfirmation, TMsgDlgButtons() <<
mbOK, 0);
}
}

> I realize its non-standard, but I've got a situation where
> I have 15 exit points from a function (its old code I'm
> trying to maintain so I don't want to mess with it too
> much) and putting cleanup code at each point is ugly at
> best, difficult to maintain at worst.

That is not what Chris was suggesting.

> I figured I could use the __finally block to do the
> cleanup no matter at which point the function exited.

That is exactly what Chris was suggesting, only without using __finally at
all. For example:

#include <memory>

void __fastcall TForm1::btn1Click(TObject *Sender)
{

try
{
std::auto_ptr<TForm2> t(new TForm2(NULL));
if( t->ShowModal() != mrOk )
throw Exception("Form close was not Ok!");
}
catch(const Exception &e)
{
MessageDlg(e.Message, mtConfirmation, TMsgDlgButtons() << mbOK,
0);
}
}

No matter how the 'try' block is exited, the auto_ptr object will go out of
scope at some point, and when it does it will automatically free the form
instance for you.


Gambit


Chris Uzdavinis

unread,
Sep 2, 2004, 7:07:17 PM9/2/04
to
Drew MacDonald <drew_...@hotmail.com> writes:

> I realize its non-standard, but I've got a situation where I have
> 15 exit points from a function (its old code I'm trying to maintain
> so I don't want to mess with it too much) and putting cleanup code
> at each point is ugly at best, difficult to maintain at worst. I
> figured I could use the __finally block to do the cleanup no matter
> at which point the function exited.

Well, the finally block is not only non-standard, but it has bugs
too. There are a few cases where it just dosn't do what you'd think,
but I have to be honest, I've never seen this particular problem
before. Looks like the compiler is flat out generating the wrong
code, putting a jump to the line I marked "HERE", right to the end of
the function, completely ignoring the finally block in this case.

This is yet another reason to avoid this construct. It really is
problematic and only there because people coming from the Delphi camp
who are used to it asked really hard for it. Unfortunately, they only
got a partial solution that is less reliable than I had thought.

>
> New code which still doesn't quite do what I want:

Right. Another __finally codegen bug.

> void __fastcall TForm1::btn1Click(TObject *Sender)
> {
> ~ TForm2 *t;
>
> ~ try
> ~ {
> ~ try
> ~ {
> ~ t = new TForm2(NULL);
> ~ t->ShowModal();
> ~ throw Exception("Some kind of exception");
> ~ }
> ~ catch(...)
> ~ {
> ~ return;
> ~ }
> ~ }
> ~ __finally
> ~ {
> ~ delete(t);
> ~ MessageDlg("I got here!", mtConfirmation,
> ~ TMsgDlgButtons() << mbOK, 0);
> ~ }

> } // <<<<<<<<<<<<<<< HERE


Now I hope that is enough to convince you that in C++, using real C++
idioms instead of ported features from other languages is better.
Here's a far superior solution that does not use __finally at all.
Maybe it generalizes to your situation?

#include <memory>

void __fastcall TForm1::btn1Click(TObject *Sender)
{

std::auto_ptr<TForm2> t(new TForm2(NULL));

t->ShowModal();
}

Now, if ShowModal() somehow does exit with an exception, you don't
really want to just catch it and ignore it, do you? You really just
want to clean up, and storing the pointer in a smart pointer, like
auto_ptr, automatically deletes the pointer when its scope is exited.

Since auto_ptr calls delete on its pointer, you can't stick arrays in
it (since they're allocated with new[], and need delete[] called on
them.) But auto_ptr is still very useful for releasing resources as a
stack frame is exited.

Using it for other tasks can also be convenient, but auto_ptr has some
weird behavior when copied that sometimes is surprising, so I prefer
to use it in the simplest of cases like the above, and use something
else for more complicated management.

This example can scale well, too:

#include <memory>

void __fastcall TForm1::btn1Click(TObject *Sender)
{

std::auto_ptr<TForm2> t(new TForm2(NULL));

std::auto_ptr<Foo> f(new Foo);
t->ShowModal();
// f automatically deleted by auto_ptr destructor
// t automatically deleted by auto_ptr destructor
}

Etc. If allocating Foo throws, or Foo's constructor throws, then the
auto_ptr t will be properly deleted. If t->ShowModal throws, then
both f and t will be destructed, which deletes the underlying pointer.

--
Chris (TeamB);

vavan

unread,
Sep 3, 2004, 3:00:14 AM9/3/04
to
On Thu, 02 Sep 2004 14:23:38 -0700, Drew MacDonald
<drew_...@hotmail.com> wrote:

>I am having a bit of a problem understanding how the try -
>__finally blocks work. My understanding was (based on the

you should never use __finally since it is broken in bcb
see several reports in QC

--
Vladimir Ulchenko aka vavan

Bob Gonder

unread,
Sep 3, 2004, 11:23:03 AM9/3/04
to
Drew MacDonald wrote:

>void __fastcall TForm1::btn1Click(TObject *Sender)
>{
>~ TForm2 *t;
>~ try {

>~ try {


>~ t = new TForm2(NULL);

>~ throw;
>~ }
>~ catch(...)
>~ {
>~ return;

This looks to me like you are returning from btn1Click.
Thought you wanted to drop through to _finally.

Chris Uzdavinis

unread,
Sep 3, 2004, 11:31:31 AM9/3/04
to
Bob Gonder <no...@notmindspring.invalid> writes:

But the documentation for __finally says it'll be "guaranteed" to
execute no matter how the function exits. It should be run even if
you return prematurely from the function. But apparently, it
doesn't.

--
Chris (TeamB);

Andrue Cope [TeamB]

unread,
Sep 3, 2004, 11:54:21 AM9/3/04
to
Chris Uzdavinis (TeamB) wrote:

> It should be run even if
> you return prematurely from the function. But apparently, it
> doesn't.

Lol. __finally has a long and respected history of not quite doing what
it should :)

--
Andrue Cope [TeamB]
[Bicester, Uk]
http://info.borland.com/newsgroups/guide.html

Bob Gonder

unread,
Sep 3, 2004, 12:02:26 PM9/3/04
to
Chris Uzdavinis wrote:

>Bob Gonder writes:
>
>>>~ try {
>>>~ t = new TForm2(NULL);
>>>~ throw;
>>>~ }
>>>~ catch(...)
>>>~ {
>>>~ return;
>>
>> This looks to me like you are returning from btn1Click.
>> Thought you wanted to drop through to _finally.
>
>But the documentation for __finally says it'll be "guaranteed" to
>execute no matter how the function exits. It should be run even if
>you return prematurely from the function. But apparently, it
>doesn't.

That's not exactly what it says.
"The __finally keyword specifies actions that should be taken
regardless of how the flow within the preceding __try exits."

Particular attention to "how the flow within".
Return aborts "the flow".
"The flow" doesn't exit.
Also says "should be taken" instead of "will be taken".
I see no "strong guarantee" in that documentation.

If you say that "return" doesn't acutally return, but is now a "goto
_finally", well, that just breaks symantics. You "return" from
functions, not from blocks. The keyword for "returning" from a block
is "break". (But only in certain cases, this not being one of them as
it is not needed because falling out of the catch will take you to
_finally.)


Chris Uzdavinis

unread,
Sep 3, 2004, 1:18:49 PM9/3/04
to
Bob Gonder <no...@notmindspring.invalid> writes:

> That's not exactly what it says.
> "The __finally keyword specifies actions that should be taken
> regardless of how the flow within the preceding __try exits."

A return out of a try block is an exit from that flow of control,
though.

> Particular attention to "how the flow within".
> Return aborts "the flow".

You're reading too much into that. It's supposed to execute however
the block exits, whether by normal flow, an exception, a goto, or a
return. All of those "exit" the flow from within the try block.

> "The flow" doesn't exit.

It certainly doesn't stay in the try block!

> Also says "should be taken" instead of "will be taken".
> I see no "strong guarantee" in that documentation.

I've seen the guarantee bandied about, even though the docs apparently
don't say it. If "should" is taken to be but a hint, then the whole
point of __finally is totally meaningless. Having maybe-protection
over resources is inherently flawed.

> If you say that "return" doesn't acutally return, but is now a "goto
> _finally", well, that just breaks symantics.

The whole concept of __finally breaks semantics. It's a non-standard
feature for a reason, and cannot be made to work cleanly in all C++
constructs, without simply defining what is to happen in certain
ambiguous situations:

try {
return 1;
} __finally {
return 2;
}

What should be the return value? Etc. It's a bad idea in general to
use, because it's buggy (my position) or it's designed poorly (if the
way it works is "as designed").

> You "return" from functions, not from blocks.

But when a return expression is evaluated, it still is exiting a block
in the process of exiting the function.

> The keyword for "returning" from a block is "break". (But only in
> certain cases, this not being one of them as it is not needed
> because falling out of the catch will take you to _finally.)

__finally causes other dilemmas as well, and in general is supposed to
execute as part of the cleanup for a scope. It's supposed to run even
if you return from the corresponding try block.

This is one area where there really is no need for discussion.

--
Chris (TeamB);

Bob Gonder

unread,
Sep 3, 2004, 8:47:22 PM9/3/04
to
Chris Uzdavinis wrote:
>Bob Gonder writes:
>
> try {
> return 1;
> } __finally {
> return 2;
> }
>
>What should be the return value?

Obviously 1 unless a throw occurs, then it's 2.
(well, intended answers, at least)
But it's bad programming.
If you have conditional return values, then you use a local variable
to hold the value, and return it at the end.

bool ReturnOne=false;
> try {
ReturnOne=true;
> } __finally {
return ReturnOne?1:2;
> }


> Etc. It's a bad idea in general to
>use, because it's buggy (my position) or it's designed poorly (if the
>way it works is "as designed").

It's the user that's buggy.
Don't try to return from inside a block unless you want to return from
the function.
It's basic symantics.

>> The keyword for "returning" from a block is "break". (But only in
>> certain cases, this not being one of them as it is not needed
>> because falling out of the catch will take you to _finally.)
>
>__finally causes other dilemmas as well, and in general is supposed to
>execute as part of the cleanup for a scope. It's supposed to run even
>if you return from the corresponding try block.

Still don't see why anyone would want to return from a try block if
they have a _finally that needs executing. Bad programming IMO.
If you need to short circut the _try, either use the dreaded goto to
the bottom of the _try, or normal block controls to bypass the
unwanted code.

>This is one area where there really is no need for discussion.

Yep. Just don't use return with a pending _finally.
I really looks odd.
If it's legal, then that's one of those "ugly" hacks you've mentioned.


Drew MacDonald

unread,
Sep 7, 2004, 12:24:46 PM9/7/04
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Remy Lebeau (TeamB) wrote:
|>I am not seeing the message dialog that is supposed
|>to be displayed in my finally block.
|
| Try not calling 'return' from inside a try...finally block. It don't
think
| it works correctly in C++. Use this code instead:

That worked, thanks very much. And thanks for the info on auto_prt.
I'll look at putting it in this program and I'll definitely include
it in future work that I do.

Thanks again,


Drew.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBPeDO+cCIvS8c+5MRAl15AJ9lnjcSvzgtzpB2MrS4/VTisxEqbACfZbuY
G1JhIG+WtKp5M2mjTeucNhQ=
=etZV
-----END PGP SIGNATURE-----

Harold Howe [TeamB]

unread,
Sep 7, 2004, 4:19:39 PM9/7/04
to
From a technical standpoint, do everything that Chris said to do in his
post.

Now for a small personal suggestion. __finally in BCB is a Borland
specific nicety. Most other C++ compilers won't have it. Given that
Borland refuses to release a roadmap for its C++ product line, and that
the last official company statement was that BCB has reached the end of
the line, it does not make sense to write code today that depends on
Borland specific extensions unless you absolutely have to.

You never have to use try/__finally. There is always another way. Yes, I
know the docs make __finally sound like a great feature, ignore them on
this one, the docs have no personal stake in your future well being.

Don't use try/__finally. You will be glad you didn't in the long run.

H^2

Drew MacDonald

unread,
Sep 7, 2004, 7:41:33 PM9/7/04
to
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Harold Howe [TeamB] wrote:

| From a technical standpoint, do everything that Chris said to do in his
| post.

Oh don't worry, I will try my best to.

| Now for a small personal suggestion. __finally in BCB is a Borland
| specific nicety. Most other C++ compilers won't have it. Given that
| Borland refuses to release a roadmap for its C++ product line, and that
| the last official company statement was that BCB has reached the end of
| the line, it does not make sense to write code today that depends on
| Borland specific extensions unless you absolutely have to.
|
| You never have to use try/__finally. There is always another way. Yes, I
| know the docs make __finally sound like a great feature, ignore them on
| this one, the docs have no personal stake in your future well being.

I knew it looked too good to be true. "Hey, all I have to do is wrap
the code in a try/__finally block and voila!!". While it solves the
immediate problem, I very much appreciate the future implications of
using vendor-specific extensions.

Its too bad that Borland has killed the BCB line. I come from a C and
assembler background and originally tried VC++ 4 or 5 for a first
attempt at a GUI. I hated it and quickly retreated back into command
line interfaces. I was forced back into GUIs at my last job and they
were a BCB shop. I found it so simple and easy to build a moderately
complex interface and program that when I came to my current job, I
did my best to convince them to go with BCB (which they did). In
hindsight, that may not have been the best plan. Oh well, c'est la
vie, I guess.

| Don't use try/__finally. You will be glad you didn't in the long run.
|
| H^2

Thanks very much for all the help everyone.


Drew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (MingW32)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFBPkct+cCIvS8c+5MRApnjAJ9DcTJMOcDhb+T8QP2R6EG7tYggBwCfXYeG
pOvs5jTW6RYGHNzo0EOFrJ8=
=OvLV
-----END PGP SIGNATURE-----

Chris Uzdavinis

unread,
Sep 23, 2004, 4:13:01 PM9/23/04
to
Bob Gonder <no...@notmindspring.invalid> writes:

>>But the documentation for __finally says it'll be "guaranteed" to
>>execute no matter how the function exits. It should be run even if
>>you return prematurely from the function. But apparently, it
>>doesn't.
>
> That's not exactly what it says.
> "The __finally keyword specifies actions that should be taken
> regardless of how the flow within the preceding __try exits."
>
> Particular attention to "how the flow within".
> Return aborts "the flow".
> "The flow" doesn't exit.
> Also says "should be taken" instead of "will be taken".
> I see no "strong guarantee" in that documentation.
>
> If you say that "return" doesn't acutally return, but is now a "goto
> _finally", well, that just breaks symantics. You "return" from
> functions, not from blocks. The keyword for "returning" from a block
> is "break". (But only in certain cases, this not being one of them as
> it is not needed because falling out of the catch will take you to
> _finally.)

As a followup, while I was at Borland recently, I had dinner with
their main compiler developer and I asked him about this topic.
"Should a return statement inside the "try" part of a try..finally
construct ever skip the finally block?"

The answer was unequivocally "no", and that if the finally block was
ever skipped then it's a compiler bug.

Since this is a non-standard feature that is designed by Borland, they
have fully liberty in deciding how it should behave, and so his answer
is 100% authorative.

I still recommend sticking to pure C++ idioms to solve this
problem. :)

--
Chris (TeamB);

0 new messages