help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: How to exit out of a function ? what is try-catch-throw in terms of


From: Alf P. Steinbach
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
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20051201 Thunderbird/1.5 Mnenhy/0.7.3.0

* 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 via email to

[Prev in Thread] Current Thread [Next in Thread]