But you commented that is can be done in correct, portable C.
Would you care to elaborate?
Make the program use explicit continuations, then do it the same way
you'd do it if explicit continuations were built into the langauge.
(Doing this in C will require at least a partial CPS transform, since
any code that can cross a continuation capture or invocation boundary
needs to be able to return to the top-level trampoline loop and whatever
comes after that point needs to be accessible from the top level.)
So the program would run from a trampoline loop that looks not entirely
unlike:
--------
while(next_continuation)
{
next_continuation=invoke_continuation(next_continuation);
}
--------
Then a normal function call would look something like:
--------
/*cont.args needs to be populated; for simplicity I'll assume
it's a void function
*/
cont->nargs=1; /*only the return continuation, no "real" args*/
cont->args[0]=magic_conversion(what_happens_next,current_state);
cont->func=called_function;
return cont;
--------
The called function would need to expect a return continuation as one
of its arguments, and it would return to its caller by doing:
--------
/*ret_cont is the continuation we got as an argument*/
ret_cont->nargs=1;
ret_cont->args[0]=magic_conversion(return_value);
return ret_cont;
--------
(Note that this is exactly the same mechanism that we used to call a
function. In a language like Scheme, the code looks exactly the same,
and in fact doesn't even need to know at the point of the call whether
it's calling a continuation or an ordinary function.)
To allow a function to return to arbitrary points in the code, simply
create a continuation for each point it might need to return to (either
by capturing a normally-existing one or by creating one for that point)
and make all of those available to the function. Then it can decide
where it wants to return to and just return the continuation for that
return point as its "next continuation".
(Once you have this, you can implement setjmp and longjmp on top of it
fairly easily; setjmp will save its return continuation in the jmp_buf
before invoking it, and longjmp will just invoke it again. The hard
part will be making sure you don't leak any temporary storage allocated
in between, since you can't just reset the stack pointer like you could
with a more typical stack-in-contiguous-memory implementation of the
function call stack.)
This is a lot of work and you end up with ugly code that's going to
be hard to understand for anybody who hasn't been exposed to the idea
before. Far better to just write code that needs it in a language that
has built-in support for it.
dave
(The biggest strength of C is that you can create any high-level
abstraction you want to use yourself; the biggest weakness of C is that
you have to create any high-level abstraction you want to use yourself.)
--
Dave Vandervies (e-mail address removed)
Basically, there is no control structure you can imagine that
can't be implemented using call/cc. Even very silly ones.
--Bear in comp.lang.scheme