C++ exception handling for Smalltalkers

David Simmons david.simmons at smallscript.com
Sat Mar 1 19:58:47 UTC 2003


"Bill Schwab" <bills at anest4.anest.ufl.edu> wrote in message
news:b3ofs4$1n9ehh$1 at ID-58434.news.dfncis.de...
> Blair,
> 
> This is slightly off-topic, but not really given that just about any
> Smalltalk needs the occaisional boost from a more staticically-typed
> language, and because I have C++ questions that (I suspect) only other
> Smalltalkers will understand.  I recently did some C++ programming that
> reminded me of my lack of knowledge about/confidence in C++ exception
> handling.
> 
> You won't like it<g>, but I've been using MinGW lately, falling back on
VC++
> for things that cause trouble otherwise.  However, most if not all of my
> coding experience with C++ excpetions has been with MFC macros.  The C++
> books on my shelf at home describe try/catch, but Google searches quickly
> turn up different implementations and question the thread safety of at
least
> some of them.  Is C++ exception handling in chaos, or are the posters
> confused?  Do you recommend any particular implementation or know of good
> reading on the subject?
> 
> Apparently one has the choice to pass exception arguments as pointers vs.
> references, and there _must_ be issues with memory management.  Any
> recommendations on the best way to proceed?  [See what I mean about having
> to ask these questions of Smalltalkers? :)]
> 
> What does one do for an analog to #ensure:?  #ifCurtailed:?

Hi Bill,

I regularly use c++ exceptions [and Window SEH and .NET mechanisms] in
addition to those within Smalltalk/S#.

Standard c++ does not provide for the notion of "resumable" exceptions, VC++
using the underlying Microsoft SEH (structured exception handle) platform
model does provide this support.

The S# language implementation (Smalltalk design) is layered on the SEH
model (with some optimizations), which means you can catch any exception
(Smalltalk or otherwise) and manage it. [sorry for the little S# commentary,
but it was worth noting].

In standard c++, once the exception "hander" has been invoked for a
particular "guard" expression, all intervening method frames (from the point
of the exception on up the stack to the guarded-method frame) have
effectively been unwound already [see temporal-issues questions regarding
c++ standard at the end]..

In c++ (as you know) the full expression form is:

    try
    {
    }
    catch("guard filter expression => var-type + optionally a var")
    {
        "handler code"
    }

In VC++ we can use the extended forms:

    __try
    {
    }
    __except(ExceptionFilter(GetExceptionInformation()))
    {
    }
    __finally
    {
    }

The extended form in vc++ allows us to provide a function for the
"guard-filter". That filter serves two purposes. First it allows us to
algorithmically take action regarding the "Signal/Warning/Exception" before
the frames between the faulting method and the guard have been unwound, and
it allows us to tell the exception mechanism what to do once our filter is
finished.

I.e., resume-execution, continue-looking-for-a-suitable-handler,
stop-and-call-my-handler.

Which means that (resumable) Smalltalk style exception handling is actually
implemented in the vc++/SEH style "guard-filter" itself, whereas c++
handling is implemented in the "handler" block of SEH behavior.

The second thing we notice about the extended form is that we have a
"finally" clause which is used to declare code we want to have executed
whenever the "try" block finishes "OR" is prematurely terminated due to an
exception fault unwinding it.

So, to distinguish between a "curtailed" and a "non-curtailed" Smalltalk
style exception unwind we would have to add a little to the sequence.
    bool wasCurtailed = true;
    __try
    {
        ... our try body here ...

        wasCurtailed = false;
    }
    __except(ExceptionFilter(GetExceptionInformation()))
    {
    }
    __finally
    {
        if(wasCurtailed)
           ...
        else
           ...
    }

I should also add that all versions of XP and later provide for dynamic SEH
exception filters (first chance filters) which can be registered on a thread
and will come ahead of normally stack-discipline exception guards. This
latter capability is very useful for many operations where you need to
handle/hide extrinsically triggered exceptions which c++ model code cannot
deal with. I should also point out that c++ only traps c++ exceptions,
whereas the VC++ extended form allows capturing of signals, warnings,
exceptions etc which includes interrupts (including INT3/DebugBreak),
faults, and the like.

Microsoft's .NET CLR (common language runtime) mechanisms are fairly similar
to the SEH model except (for non-specific reasons) the guard-filter does not
support "resume-execution" semantics. 

>From .NET IL we have:

    .try <il_start_range> to <il_end_range>
         filter <il_filter_label>
             handler <il_start_range> to <il_end_range>
         // catch "<type> handler ..." are optional here 
         finally
             handler <il_start_range> to <il_end_range>
         fault
             handler <il_start_range> to <il_end_range>.

Generally coded as:

   .try
    {
        ...
    }
    filter
    {
        ...
        endfilter
    } "handler" {
        ...
    }
    finally
    {
        ...
    }
    fault 
    { 
        ...
    }

This effectively corresponds to S# equivalents of the form:

    try [
    ]
    "we can have multiple catch clauses and discrete handlers here"
    catch("filter-expression including closures etc") [
        "handler is executed during the filter"
        "if recoded in .NET/VC++ handler body is no-op"
    ]
    finally [
    ]
    fault "or ifCurtailed" [
    ].

Where the keyword variable <thisSignal> can be referenced where needed, or a
variable to hold <thisSignal> can be declared as a parameter to a handler
block.

The (typically less-efficient/more-cumbersome-nesting) classic resumable
ANSI Smalltalk exceptions subset of the above would look like:

    [
        [
            [
                ...place actual try body here...
            ] 
            on: exceptionFilter do: [:signal|
                ...exception handler body...
            ].
        ] 
        ifCurtailed: [:signal|
            ...curtailed/fault handler body...
        ]
     ]
     ensure: [:signal|
         ...finally handler body...
     ].

As to the question of "pointer" versus "new-inst" form with exceptions, you
do need to be careful. It really helps to understand the temporal semantics
in question for c++ [which, as I now realize in writing this -- see below --
I am not so sure about].

Which is that the given exception-handler is logically invoked "after" all
the intervening frames have been unwound. In practice they will probably not
have been unwound yet (come to think about it, I'm sure the c++ spec must
say something about this timing).

What that means is that it is probably safe to use an "auto/stack" allocated
exception in a throw since the stack has not actually been unwound when the
guard is invoked. But if stack unwind semantics (maybe specified in C++
standard) result in the destructor being called on the (auto/stack)
allocated exception before you get it, then you are getting an exception
structure whose memory still exists but which has already had its destructor
invoked. I.e., generally the destructors in an exception do nothing so this
is a non-issue.

I could go look at the c++ spec/tome for clarification, but I will leave
this to someone with a bit more time for such digging.

Cheers,

-- Dave S.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
S# for the AOS & .NET Platforms
David.Simmons at SmallScript.com
http://www.smallscript.org

> 
> Have a good one,
> 
> Bill
> 
> --
> Wilhelm K. Schwab, Ph.D.
> bills at anest4.anest.ufl.edu
> 
> 
>



More information about the Squeak-dev mailing list