guile-user
[Top][All Lists]
Advanced

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

Re: C++ and stack unwinding/destruction


From: Marco Maggi
Subject: Re: C++ and stack unwinding/destruction
Date: Sat, 17 Mar 2007 08:27:42 +0100

"David Fang" wrote:
> C++ relies heavily on constructor-destructor duality
> [...]
> The implementation of guile's C-style exceptions using
> setjmp/longjmp, however breaks this universal law.
> [...]
> In the event of scm_error_scm(), the destructors that
> *should* be called in the frame of this function are
> just skipped.

I do not have much time to dig into the list's archive,
so I do not know what is in the inconclusive thread;
anyway, you have to use 'scm_c_catch()' and the C++'s
'catch'.

You have to separate the Guile/C code from the C++ code:

* when calling C++ from Guile/C: put C++ code inside
  a 'catch' and convert the C++ exception into the
  Guile/C arguments to 'scm_error()';

* when calling Guile/C from C++: put the Guile/C code
  into a separate function and use 'scm_c_catch()' in
  the C++ function to invoke it.

I do it all the time when I register a Guile/C callback
into an external C library and I cannot let the dynwind
mechanism go through the foreign functions.

When I need to invoke a Scheme function from the callback
I use something like this:

  typedef struct frame_t {
    SCM    s_function;
    SCM    s_data_to_function;
    SCM    s_error_key;
    SCM    s_error_args;
    SCM    s_error_stack;
  } frame_t;

  static SCM
  catch_handler (void * data, SCM s_error_key,
                 SCM s_error_args)
  {
    frame_t * frame = data;

    frame->s_error_key  = s_error_key;
    frame->s_error_args = s_error_args;
    return SCM_BOOL_F;
  }
  static SCM
  pre_unwind_handler (void * data, SCM s_dummy1,
                      SCM s_dummy2)
  {
    frame_t * frame = data;

    frame->s_error_stack = scm_make_stack(SCM_BOOL_T,

                                          SCM_EOL);
    return SCM_BOOL_F;
  }
  static SCM
  function_caller (void * params)
  {
    frame_t * frame = params;
    SCM       s_result;

    s_result = scm_call_1(frame->s_function,
                          frame->s_data_to_function);
    /* validate 's_result' here */
    return s_result;
  }
  static type_t
  callback_function (void * data, void * params)
  {
    frame_t * frame = params;
    SCM       s_result;

    frame->s_error_key   = SCM_UNSPECIFIED;
    frame->s_error_args  = SCM_UNSPECIFIED;
    frame->s_error_stack = SCM_UNSPECIFIED;

    frame->s_data_to_function = something_from(data);
    s_result = scm_c_catch(SCM_BOOL_T,
                           function_caller,    frame,
                           catch_handler,      frame,
                           pre_unwind_handler, frame);

    if (SCM_UNSPECIFIED != frame->s_error_key)
      /* there was an error */
    else
      return type_from(s_result);
  }

when I register the callback I allocate a 'frame_t' and
initialise the 's_function' field with the SCM procedure
SMOB.

If later I need to retrhow the Guile error I use:

  void
  gee_rethrow_error_with_stack (SCM s_error_stack,
                                SCM s_error_key,
                                SCM s_error_args,
                                const char * procname)
  {
    SCM    s_port, s_string, s_stack, s_data;
    size_t len;

    s_stack = scm_make_stack(SCM_BOOL_T, SCM_EOL);
    len =
      scm_to_uint(scm_stack_length(s_error_stack)) -
      scm_to_uint(scm_stack_length(s_stack)) - 1;

    s_port = scm_open_output_string();
    {
      SCM s_message =
        scm_list_ref(s_error_args,scm_from_uint(1));
      SCM s_args =
        scm_list_ref(s_error_args,scm_from_uint(2));

      scm_simple_format(s_port, s_message,
                        (scm_is_eq(SCM_BOOL_F,s_args)?
                          SCM_EOL : s_args));
      scm_newline(s_port);
      scm_display_backtrace(s_error_stack, s_port,
                            scm_from_uint(len),
                            SCM_UNDEFINED);
      s_string = scm_get_output_string(s_port);
    }
    scm_close_output_port(s_port);
    s_data = scm_list_ref(s_error_args, scm_from_uint(3));
    scm_error_scm(s_error_key,
                  scm_from_locale_string(procname),
                  s_string, SCM_BOOL_F, s_data);
  }

HTH

--
Marco Maggi






reply via email to

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