This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [basic-improvements] try/finally support for c/c++ - more tests
- From: Jakub Jelinek <jakub at redhat dot com>
- To: Nicola Pero <nicola at brainstorm dot co dot uk>
- Cc: Mark Mitchell <mark at codesourcery dot com>, Zack Weinberg <zack at codesourcery dot com>, "gcc-patches at gcc dot gnu dot org" <gcc-patches at gcc dot gnu dot org>
- Date: Fri, 8 Nov 2002 08:46:59 -0500
- Subject: Re: [basic-improvements] try/finally support for c/c++ - more tests
- References: <147270000.1036722436@warlock.codesourcery.com> <Pine.LNX.4.21.0211081158290.17515-100000@nicola.brainstorm.co.uk>
- Reply-to: Jakub Jelinek <jakub at redhat dot com>
On Fri, Nov 08, 2002 at 12:48:49PM +0000, Nicola Pero wrote:
> Well you're not a lone voice of insance conservatism, I actually think you
> are quite well giving voices to the puzzled C users, like me. :-)
>
> In all this discussion, I didn't quite find discussed how it would work in
> C/ObjC, from a pure C/ObjC perspective. Which IMO is essential to have
> the C extension approved.
>
> The suggestion that this change is good because "it makes C look more like
> C++ so that it's easier to integrate C and C++" might be good for C++
> hackers and lovers, but it's definitely unconvincing for C/ObjC users with
> no interest in C++.
If you look at a typical system nowadays, C and C++ has to easily integrate
together, as C++ code often calls C routines (starting from libstdc++ using
C library functions where C library is most often written in C) and a lot of
C programs/libraries are using C++ libraries too.
> Shall we "finally try" to talk a bit about this C language extension from
> a C user perspective, without jumping into C++ every sentence ?
>
> I've got a lot of doubts that this extension is good for C, at least as it
> is.
>
> In C there is no exception handling, so it's common for C libraries to
> implement some sort of exception handling using setjmp/longjmp.
>
> As far as I understand, the proposed feature wouldn't be enough powerful
> to replace the existing setjmp/longjmp based exception handling.
It has no intent to replace it, it is about cleanups when exception is
thrown, not the exception handling mechanism itself.
> Actually, the proposed feature would actually not work with the existing
> setjmp/longjmp based exception handling ... either you use the new feature
> (which is not enough powerful to provide exception handling), or you use
> setjmp/longjmp.
But it plays nicely with setjmp/longjmp_unwind. Code using pure
setjmp/longjmp nowadays cannot use any cleanups locally, all buffers you
allocate or locks you acquire have to be global, so that when setjmp returns
non-zero you can free them or release them. At least with current glibc
pthread_cleanup_* implementation, it even cannot use pthread_cleanup_*,
because libpthread will be seriously confused (usually segfault) after
you try longjmp for the first time skipping some frame which did
pthread_cleanup_push.
> As a C/ObjC user, I don't find this solution very satisfactorily. We've
> been long waiting for "real" exception handling in C/ObjC ... and this is
> *not* it.
See the above. If you do:
jmp_buf b;
void foo (void)
{
if (setjmp (b))
{
exception was raised;
}
bar ();
}
void bar (void)
{
void *x = malloc (1024);
__try
{
do_something_with_x
baz ();
do_something_else_with_x
}
__finally
{
free (x);
}
}
void baz (void)
{
if (some_weird_condition)
longjmp_unwind (b);
}
then you IMHO have all you need for satisfactory pure C exception handling
with minimal overhead. With __thread variables you can put any
data you want into them at longjmp_unwind time (ie. throw someobject in C)
and check it at setjmp time.
Without __try/__finally but with longjmp_unwind you can still do this,
but see my other mail what needs to be done instead of that, making the code
quite unreadable.
> As far as I understand from the explanations on this thread, try/finally
> in C would only be used to cleanup before returning from a function, such
> as
>
> try
> {
> char *stringA = NULL;
> char *stringB = NULL;
> char *stringC = NULL;
>
> stringA = malloc (sizeof (char) * 20);
> if (stringA == NULL)
> return;
>
> ...
>
> stringB = malloc (sizeof (char) * 20);
> if (stringB == NULL)
> return;
>
> ...
>
> stringC = malloc (sizeof (char) * 20);
> if (stringC == NULL)
> return;
>
> ...
>
> return;
> }
> finally
> {
> if (stringA != NULL)
> free (stringA);
>
> if (stringB != NULL)
> free (stringB);
>
> if (stringC != NULL)
> free (stringC);
> }
>
> It's nice as an example, except I can trivially do it already without any
> need for a C extension:
>
> char *stringA = NULL;
> char *stringB = NULL;
> char *stringC = NULL;
>
> stringA = malloc (sizeof (char) * 20);
> if (stringA == NULL)
> goto finally;
>
> ...
>
> stringB = malloc (sizeof (char) * 20);
> goto finally;
>
> ...
>
> stringC = malloc (sizeof (char) * 20);
> if (stringC == NULL)
> goto finally;
>
> ...
>
> finally:
> {
> if (stringA != NULL)
> free (stringA);
>
> if (stringB != NULL)
> free (stringB);
>
> if (stringC != NULL)
> free (stringC);
>
> return;
> }
Sure, but:
a) typically in such functions they are not returning void, but some value,
with __try/__finally you can leave the code as above, just add the
return value to each return statement. With goto gem you need some
function scope variable and set it first, then return,
so if (stringC == NULL) return 134; becomes
if (stringC == NULL) { ret = 134; goto finally; }
which is less readable
b) if not all variables are function scope, the code gets much uglier
c) with __try/__finally, you get the cleanups with pthread cancellation
or C++ exceptions thrown or longjmp_unwind for free. You don't have
to worry about storing stringA, stringB, stringC not in
separate variables, but into some local structure, doing
pthread_cleanup_push at the beginning with address of that local structure
and either having the finally block just do pthread_cleanup_pop(1)
or pthread_cleanup_pop(0) with duplication of all the free calls into
the code after finally label and in a separate function.
Jakub