unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
From: "Alf P. Steinbach" <alfps@start.no>
To: help-gnu-emacs@gnu.org
Subject: Re: How to exit out of a function ? what is try-catch-throw in terms of Program Counter
Date: Tue, 23 Oct 2007 21:53:23 +0200	[thread overview]
Message-ID: <13hsk5u8tekg618@corp.supernews.com> (raw)
In-Reply-To: <1193162818.247085.305430@q3g2000prf.googlegroups.com>

* gnuist006@gmail.com:
> On Oct 20, 3:55 pm, "Alf P. Steinbach" <al...@start.no> wrote:
>> The closest equivalent in C would be a 'longjmp'.  However, a C++
>> exception is more limited, in that it will only jump up the call chain,
>> and it's more powerful, in that it will destroy local objects as it does
>> so.  Also, if you use 'longjmp' in C++ you're practically doomed (unless
>> you use it to jump between co-routines with their own stacks), because
>> 'longjmp' doesn't destroy local objects.
> 
> Sure you have good ideas.

Note that some people could read that as an attempt at insulting.


> I still would like an equivalent implementation explained. Ofcourse,
> smart
> companies and smart programmers were doing all this before C++ came
> and even in LISP they have atleast two of try catch throw.

Ada introduced to the wider community much that was subsequently adopted 
in C++.  Interestingly, (much of) the STL was implemented in Ada before 
it was implemented in C++.  And perhaps also interestingly, Ada's high 
level thread primitives are seemingly now /not/ considered for C++.

Now as for equivalence, you don't really want C code, because that would 
have to emulate C++ objects!

But in C++ such longjm-based code is hairy compiler-dependent stuff, 
with formally Undefined Behavior.

Also, as an example of equivalent-except-for-efficiency, note that a 
call of a virtual function can be specified equivalently as a dynamic 
lookup in most derived class, base class, base class' base class and so 
on, a search up the base class chain, but is in actuality implemented as 
a table look-up (with all compilers).  Exceptions are implemented in 
more than just one main way.  However, analogously to the case with 
virtual functions, equivalent code that performs dynamic lookup, such as 
the code below, is extremely ineffecient compared to the Real Thing(TM).

Depending on the actual implementation of exceptions, there can be no 
overhead at all for normal case code.


<code>
#include    <vector>
#include    <csetjmp>
#include    <string>
#include    <iostream>
#include    <ostream>

#if         defined( _MSC_VER )
#   define  LNGJMP_DESTROYS_AUTOMAGICALLY

#elif       defined( __GNUC__ )
#   undef   LNGJMP_DESTROYS_AUTOMAGICALLY
#   // No automatic destruction, at least in MingW 3.4.4 version.

#else
#   error Your compiler is not supported by this program.
#endif

struct AbstractLngjmpCleanup
{
     virtual ~AbstractLngjmpCleanup() {}
     virtual void destroy() = 0;
};

template< typename T >
struct LngjmpCleanup: AbstractLngjmpCleanup
{
     T*  myTarget;
     LngjmpCleanup( T& target ): myTarget( &target ) {}
     virtual void destroy()
     {
         #ifndef LNGJMP_DESTROYS_AUTOMAGICALLY
             myTarget->T::~T();      // Call destructor on target.
         #endif
     }
};

struct LongjmpCleanups
{
     std::vector<AbstractLngjmpCleanup*> myDestroyers;

     ~LongjmpCleanups()
     {
         for( size_t i = 0;  i < myDestroyers.size();  ++i )
         {
             delete myDestroyers.at( i );
         }
     }

     template< typename T >
     void add( T& target )
     {
         myDestroyers.push_back( new LngjmpCleanup<T>( target ) );
     }

     void destroyAll()
     {
         for( size_t i = 0;  i < myDestroyers.size();  ++i )
         {
             myDestroyers.at( i )->destroy();
         }
     }
};

template< typename T >
void say( T const& v ) { std::cout << v << std::endl; }

struct Whatever
{
     std::string myId;
     Whatever( std::string id ): myId( id )
     { say( "Constructed " + myId + "." ); }

     ~Whatever()
     { say( "Destroyed " + myId + "." ); }
};


jmp_buf*    pReturnAddress = 0;

void bottom()
{
     LongjmpCleanups                 destroyers;
     LngjmpCleanup<LongjmpCleanups>  destroyersDestroyer( destroyers );

     Whatever    localObject( "bottom()'s local object" );

     destroyers.add( localObject );

     say( "Executing body of bottom()." );

     say( "Throwing simulated exception." );
     {
         destroyers.destroyAll();
         destroyersDestroyer.destroy();
         longjmp( *pReturnAddress, 1 );
     }
}

void middle()
{
     jmp_buf                         returnAddress;
     jmp_buf*                        pOldReturnAddress;
     LongjmpCleanups                 destroyers;
     LngjmpCleanup<LongjmpCleanups>  destroyersDestroyer( destroyers );

     Whatever    localObject( "middle()'s local object" );

     destroyers.add( localObject );
     pOldReturnAddress = pReturnAddress;
     if( setjmp( returnAddress ) == 0 )
     {
         pReturnAddress = &returnAddress;

         say( "Executing body of middle(), calling bottom()." );
         bottom();

         pReturnAddress = pOldReturnAddress;
     }
     else
     {
         destroyers.destroyAll();
         destroyersDestroyer.destroy();
         pReturnAddress = pOldReturnAddress;
         longjmp( *pReturnAddress, 1 );
     }
}

void top()
{
     jmp_buf                         returnAddress;
     jmp_buf*                        pOldReturnAddress;
     LongjmpCleanups                 destroyers;
     LngjmpCleanup<LongjmpCleanups>  destroyersDestroyer( destroyers );

     Whatever    localObject( "top()'s local object" );

     destroyers.add( localObject );
     pOldReturnAddress = pReturnAddress;
     if( setjmp( returnAddress ) == 0 )
     {
         pReturnAddress = &returnAddress;

         say( "Executing body of top(), calling middle()." );
         middle();

         pReturnAddress = pOldReturnAddress;
     }
     else
     {
         destroyers.destroyAll();
         destroyersDestroyer.destroy();
         pReturnAddress = pOldReturnAddress;
         longjmp( *pReturnAddress, 1 );
     }
}

int main()
{
     jmp_buf     returnAddress;

     pReturnAddress = &returnAddress;
     if( setjmp( returnAddress ) == 0 )
     {
         say( "Main business code, calling top()..." );
         top();
         return EXIT_SUCCESS;
     }
     else
     {
         say( "Caught simulated exception!" );
         return EXIT_FAILURE;
     }
}
</code>

<output>
Main business code, calling top()...
Constructed top()'s local object.
Executing body of top(), calling middle().
Constructed middle()'s local object.
Executing body of middle(), calling bottom().
Constructed bottom()'s local object.
Executing body of bottom().
Throwing simulated exception.
Destroyed bottom()'s local object.
Destroyed middle()'s local object.
Destroyed top()'s local object.
Caught simulated exception!
</output>

Now I leave it as an exercise to reimplement this program to use C++ 
exceptions instead of longjmp, and perhaps compare clarity (and 
efficiency, if that's interesting).

Cheers, & hth.,

- Alf


-- 
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

  reply	other threads:[~2007-10-23 19:53 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-10-20 20:45 How to exit out of a function ? what is try-catch-throw in terms of Program Counter gnuist006
2007-10-20 20:57 ` gnuist006
2007-10-20 22:55 ` Alf P. Steinbach
2007-10-23 18:06   ` gnuist006
2007-10-23 19:53     ` Alf P. Steinbach [this message]
2007-11-04 23:02   ` David Thompson
2007-11-05  5:07     ` Alf P. Steinbach
2007-11-05  5:59       ` Alf P. Steinbach
2007-12-02 23:20       ` David Thompson
2007-12-02 23:51         ` Alf P. Steinbach
2007-10-21  1:08 ` Jim Langston
2007-10-21  2:30 ` Keith Thompson
2007-10-21 10:44   ` Kenny McCormack
2007-10-21 10:55     ` santosh
2007-10-21 12:26       ` Richard Heathfield
2007-10-21 22:35       ` Mark McIntyre
2007-10-21 12:09     ` Malcolm McLean
2007-10-21 15:23       ` Kenny McCormack
2007-10-21 15:54         ` santosh
2007-10-21 17:39         ` Malcolm McLean
2007-10-21 21:53           ` Kenny McCormack
2007-10-21  8:03 ` Malcolm McLean
2007-10-21 16:07 ` abhy
2007-10-21 17:43   ` santosh
2007-10-23  9:04 ` Joel Yliluoma
2007-10-23 16:33 ` Stefan Monnier
2007-10-23 16:44   ` gnuist006
2007-10-23 16:45   ` Victor Bazarov
2007-10-24  1:06     ` Stefan Monnier
2007-10-23 16:54   ` gnuist006
2007-10-23 17:14     ` Alf P. Steinbach
2007-10-24  1:09     ` Stefan Monnier
2007-10-24  0:02   ` Joel Yliluoma

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=13hsk5u8tekg618@corp.supernews.com \
    --to=alfps@start.no \
    --cc=help-gnu-emacs@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).