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

Re: embedding mzscheme into a C++ application



Matthew Flatt <mflatt@cs.utah.edu> writes:

> Sorry for the delay. (Sometimes mail gets buried.)

No problem.  :)  Thanks for your answers!

 
> One possibility is to diallow the jump.

What consequences follow when this route is taken?  (Other than not
being able to jump?)

> When you use the non-underscored versions of the MzScheme entry points
> (e.g., scheme_apply() instead of _scheme_apply()), full-continuation
> jumps in either direction are already disabled. That provides minimal
> sanity from the point of view of C/C++ code, which expects a single
> return from the Scheme code.

I didn't realize there was an underscore and a non-underscored version
of _scheme_apply.  That's good to know.
 
> To block the remaining jumps (i.e., escape continuation jumps), you can
> catch the longjump and ignore it. [This turns out not to be a special
> power of C code. Jumps can be blocked in Scheme, too, using
> `dynamic-wind'.]

This is what I've been doing and so far it has been working to my
satisfaction.  However, I don't really use continuations very often
yet, as they're still kind of a fuzzy thought.  Though The Seasoned
Schemer helped clairify the concpet with the let/cc calls.  :)  

> MrEd (implemented in C++) used to ignore escapes that way. But we've
> eliminated the need for any clean-up actions --- it helps to depend on
> GC --- so MrEd allows the jump.

I thought about using the GC to hold "local" variables, so that they
will properly get cleaned up.  This was the only solution I found to
embedding Ruby in C++ (and I think Ruby borrowed some of MzScheme
code, or ideas, as the implementation of many things is remarkably the
same.)  But it's a pain to do. 

> Now, I suppose you want to allow escaping jumps (e.g., for raising
> exceptions) and you really need destructor-based clean-up...

That would be ideal, but using scheme as a configuration language for
a C++ application doesn't require a lot of complex routines.  Probably
the most common exception would be a syntax error, which would mean
the program is misconfigured anyway and probably shouldn't run.

Therefore I've just had a single setjmp in main, which is a way to
exit the program and dump an error message.  Crude but effective.
(And I'm not terribly worried about leaks from a program shutting
down, since the OS will take care of it.)

> > Basically, what needs to be done in every C++ routine that interacts
> > with scheme (especially if it's reentrant from scheme, as in 1->2
> > above), it needs to catch any long jumps, clean up all local objects,
> > and then re-jump to wherever the original long jump was headed.
> > 
> > But I am not sure how to do that cleanly, since mzscheme encapsulates
> > the setjmp/longjmp routines, and I haven't bothered yet to look at
> > what they do.
> 
> I'm not up-to-date on C++: has the exception mechanism become widely
> implemented?

Depending on your compiler, yes.  g++ is very good, as are a few
others.  (I mainly use g++, though on Windows we use Borland, who also
has a decent exception implementation.)

> If so, couldn't you catch the longjmp at the C++->Scheme boundary,
> raise a C++ exception, catch the exception at the Scheme->C++ entry
> point, and longjmp again?

I think that would work.  But there is an uncertainty of what the
stack variables values are when the longjmp is "caught".  Do they
revert to the value they had at the time setjmp was called, or do they
retain their value from when they called into the next function (which
eventually lead up to the longjmp)?  We need to make sure that the
values are not rolled back, so additionally, we must declare all the
stack-based objects as being "volatile."

> That's the only idea I have at the moment.

There is still a problem with temporary variables if they exist while
scheme code executes.  But that's more of a closet-corner case, since
you'd have to code in such a way that scheme_apply() were called in
the middle of an expression that created temporary C++ objects.  But
that's just not likely to happen, unless scheme_apply() itself was
encapsulated in a C++ object, which isn't a bad idea until you think
of the potential consequences of temporaries.  :)


One final question on this issue then: if I want to implement your
suggestion, by re-longjmp-ing at the end of my C++ function, how do I
know which jump-buffer to longjmp into?  (I'm not terribly familiar
with longjmp, as it's fairly strongly discouraged in C++.)  Is there a
way to ask for the "next higher" jump buffer... which is where the
jump would have gone had I not intercepted it?

-- 
Chris