unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* C++ and stack unwinding/destruction
@ 2007-03-16 23:24 David Fang
  0 siblings, 0 replies; 6+ messages in thread
From: David Fang @ 2007-03-16 23:24 UTC (permalink / raw)
  To: guile-user

Hi all,
	My initial use with guile (1.8 and 1.6) and C++ has been pretty
satisfactory so far -- I think scheme and C++ complement each other very
well, giving the best of both worlds.  However, one serious problem
remains.
	C++ relies heavily on constructor-destructor duality, namely
that objects on the stack are destroyed in reverse order that they are
constructed.  This paradigm is what enables effective resource/memory
management, even in the face of exceptions.  The implementation of guile's
C-style exceptions using setjmp/longjmp, however breaks this universal
law.  Consider:

SCM
my_fun(void) {
my_class c;	// acquires some resources, e.g. memory, mutex
if (...) {
  scm_error_scm(...);	// setjmp exception!
}
// expect c to be destroyed w/ ~my_class() no matter how we leave
return SCM_UNSPECIFIED;
}

In the event of scm_error_scm(), the destructors that *should* be called
in the frame of this function are just skipped.  (Just try printing a
message in the destructor of my_class.)

I dug through some the the archives and found only an inconclusive thread
on "Guile & C++" (ca. 1998-99).

So that brings me to the questions:
Are guile/C++ users and developers aware of this flaw?  (wake-up call!)
Are there plans to fix this?

If I were to go about fixing this, I would consider an option to build
guile/libguile C++-mode replacing the setjmp/longjmp business with
first-class C++ exceptions (throw/catch).  I'm eyeing error.c and throw.c
as places to consider making the initial surgical edits.

However, to make this unintrusive to pure C users, I'd propose an option
such as --enable-cxx to build the C++-safe variant of guile/libguile
(maybe named differently, libguilexx.{a,so,dylib}).


Side note, I'm developing a template library to simplify conversions
between statically-typed STL containers and SCM types.  Has this already
been done, or is there any interest in the community?


Fang



David Fang
Computer Systems Laboratory
Electrical & Computer Engineering
Cornell University
http://www.csl.cornell.edu/~fang/
	-- (2400 baud? Netscape 3.0?? lynx??? No problem!)



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: C++ and stack unwinding/destruction
@ 2007-03-17  7:27 Marco Maggi
  2007-03-20  5:11 ` David Fang
  0 siblings, 1 reply; 6+ messages in thread
From: Marco Maggi @ 2007-03-17  7:27 UTC (permalink / raw)
  To: guile-user

"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




_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: C++ and stack unwinding/destruction
  2007-03-17  7:27 C++ and stack unwinding/destruction Marco Maggi
@ 2007-03-20  5:11 ` David Fang
  0 siblings, 0 replies; 6+ messages in thread
From: David Fang @ 2007-03-20  5:11 UTC (permalink / raw)
  To: Marco Maggi; +Cc: guile-user

> "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'.

Hi,
	First, thanks for the informative answer.

> 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.

	So this means I should be able to stop the dynwind from plowing
through any pure C++ function, in theory, right?  I was hoping there would
be something cleaner, but having some way is better than none.  I'm just
saddened that the C++ semantics I could asssume for 'free' (compiler
managed) would be as automatic, but this is C we're talking about.  :-/

> When I need to invoke a Scheme function from the callback
> I use something like this:
>
--------------->8 snip 8<---------------
>
> 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:
--------------->8 snip 8<---------------

	I'll digest your example some more and hopefully give it a shot.
(I'm still itching to upgrade the libguile API for C++, if I only had the
time to futz with it...)

David Fang
Computer Systems Laboratory
Electrical & Computer Engineering
Cornell University
http://www.csl.cornell.edu/~fang/
	-- (2400 baud? Netscape 3.0?? lynx??? No problem!)



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: C++ and stack unwinding/destruction
@ 2007-03-20  7:17 Marco Maggi
  2007-03-21 23:11 ` David Fang
  0 siblings, 1 reply; 6+ messages in thread
From: Marco Maggi @ 2007-03-20  7:17 UTC (permalink / raw)
  To: David Fang; +Cc: guile-user

"David Fang" wrote:
> So this means I should be able to stop the dynwind from
> plowing through any pure C++ function, in theory, right?

Yes.

> (I'm still itching to upgrade the libguile API for C++,
> if I only had the time to futz with it...)

IMHO this is possible only by wrapping libguile with
a C++ library. And even in this case mixing the two
dynwinds is troublesome. I am not a C++ programmer,
but maybe using object functions to invoke the Guile/C
API and wrapping them in scm_dynwind_begin/end...

--
Marco Maggi




_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: C++ and stack unwinding/destruction
  2007-03-20  7:17 Marco Maggi
@ 2007-03-21 23:11 ` David Fang
  0 siblings, 0 replies; 6+ messages in thread
From: David Fang @ 2007-03-21 23:11 UTC (permalink / raw)
  To: Marco Maggi; +Cc: guile-user

> > So this means I should be able to stop the dynwind from
> > plowing through any pure C++ function, in theory, right?
>
> Yes.
>
> > (I'm still itching to upgrade the libguile API for C++,
> > if I only had the time to futz with it...)
>
> IMHO this is possible only by wrapping libguile with
> a C++ library. And even in this case mixing the two
> dynwinds is troublesome. I am not a C++ programmer,
> but maybe using object functions to invoke the Guile/C
> API and wrapping them in scm_dynwind_begin/end...

Hi,
	One possible approach could be to wrap top-level Guile/C calls in
C++ to convert dynwind exceptions into C++ exceptions, but isolate C++
exceptions to prevent them from espacing to guile/C by "rethrow"-ing with
scm_error or equivalent.
	e.g.

SCM
my_cxx_function(SCM arg1) {
	try {
		// actual function body that is protected from dynwinding
		// through upon error via some handler...
	} catch (...) {
		// helpful dianostic
		scm_error_scm(...);
	}
}

It the idea sound? or just crazy?

Fang



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: C++ and stack unwinding/destruction
@ 2007-03-23  6:35 Marco Maggi
  0 siblings, 0 replies; 6+ messages in thread
From: Marco Maggi @ 2007-03-23  6:35 UTC (permalink / raw)
  To: guile-user

"David Fang" wrote:
> One possible approach could be to wrap top-level Guile/C
> calls in C++ to convert dynwind exceptions into C++
> exceptions, but isolate C++ exceptions to prevent them
> from espacing to guile/C by "rethrow"-ing with scm_error
> or equivalent.

Yes, exactly what I told you in my first answer.

--
Marco Maggi

"They say jump!, you say how high?"
Rage Against the Machine - "Bullet in the Head"



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://lists.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2007-03-23  6:35 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-17  7:27 C++ and stack unwinding/destruction Marco Maggi
2007-03-20  5:11 ` David Fang
  -- strict thread matches above, loose matches on Subject: below --
2007-03-23  6:35 Marco Maggi
2007-03-20  7:17 Marco Maggi
2007-03-21 23:11 ` David Fang
2007-03-16 23:24 David Fang

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).