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