
Call/cc for C programmers (2010) - fao_
http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers
======
csl
It says it's just setjmp/longjmp, but later on expands on that, saying you can
jump not only _up_ the stack, but down and sideways too (#1).

I think the best explanation (mentioned in the article) is that you take a
copy of the entire call stack, and can then reinstate it whenever you want to.

An even simpler explanation is Guy L. Steele's that it's "gotos with
parameters", that is, basically goto, but let's you pass a parameter to the
destination:

    
    
        (define saved-continuation #f)
    
        (display (string-append "hello "
                    (call/cc (lambda (here)
                      (set! saved-continuation here)
                      "world"))
                    "!\n"))
        ; Displays: "hello world!"
    
        (saved-continuation "everyone")
        ; Displays "hello everyone!"
    

Running it with guile:

    
    
        $ guile cc.scm
        hello world!
        hello everyone!
    

This code makes `saved-continuation` _equivalent_ to

    
    
        (lambda (param)
                (display (string-append "hello " param "!\n"))))
    

Matt Might has an explanation, too:
[http://matt.might.net/articles/programming-with-
continuation...](http://matt.might.net/articles/programming-with-continuations
--exceptions-backtracking-search-threads-generators-coroutines/)

Finally, we have delimited continuations, which lets you basically slice off a
portion of the current call stack, instead of taking it in its entirety. It's
more powerful and lets one create true functions, and implement any other
control flow construct.

#1: What's often not mentioned is the fact that you can only jump to code
you've already run. So you can't jump straight into somewhere your program has
not been, yet.

~~~
iso8859-1
You can't reinstate the stack after restarting the process, either. Which
would be nice, for saving state in a cross-platform way (not using core
dumps).

~~~
jpolitz
For an approach that does this, check out how Racket's web server manages
statelessness across requests:

[http://docs.racket-lang.org/web-
server/stateless.html#%28par...](http://docs.racket-lang.org/web-
server/stateless.html#%28part._considerations%29)

[http://cs.brown.edu/~sk/Publications/Papers/Published/pcmkf-...](http://cs.brown.edu/~sk/Publications/Papers/Published/pcmkf-
cont-from-gen-stack-insp/paper.pdf)

~~~
iso8859-1
That paper is great, thanks! Can you recommend any recent papers regarding
this subject? That paper was from ICFP '05.

~~~
jpolitz
Off the top of my head, I'm not sure (other than follow-up work to this paper,
like
[http://jeapostrophe.github.io/home/static/icfp065-mccarthy.p...](http://jeapostrophe.github.io/home/static/icfp065-mccarthy.pdf)
[ICFP '09]). But, the ideas in that paper aren‘t any less true just because
they are from 2005, and they are still what the Racket web server runs on as
far as I know :-)

------
jarcane
_When Scheme folks get hot and bothered about continuations not being gotos
what they normally mean is that the idea of a continuation is much more
fundamental._

I've found a much more elemental way of thinking of it is that it's not goto,
but rather a tool you can use to make a goto. Or a break, or a yield, or
coroutines like is mentioned in the article, and so on. Racket's web framework
does an insane amount of clever things with continuations for everything from
concurrent request handling to templating forms in functions that return HTML.

It gives you first-class control over control structures to a degree that
still, frankly, seems like magic to me at times even though I've used
continuations in several projects now.

------
kazinator
> _But in Scheme you can jump back down as well, or even "sideways". _

In C you can certainly jump "sideways". The condition for using longjmp is
that the function where the context had been saved has not terminated. This
means a function can longjmp to itself (regardless of block nesting).

Ah, I see in C99 that an additional condition had been added: you can't
longjmp to a block which has terminated, if that block contains the
declaration of an identifier with variably modified type (i.e. VLA).

~~~
JoshTriplett
> In C you can certainly jump "sideways". The condition for using longjmp is
> that the function where the context had been saved has not terminated. This
> means a function can longjmp to itself (regardless of block nesting).

However, you can't longjmp to a function that's a "sibling" in the call tree:
if f() calls a(), then calls b(), b can't longjmp to a, though it can longjmp
to f.

~~~
kazinator
That's covered by "can't call down". f() can't jump back into a() after a()
returns, and that restriction isn't going to go away if b() is called. (If
anything, the actual behavior is worse because b() will blindly execute over
the a() stack frame, whereas if f() calls a(), it's actually possible (though
not ISO-C-defined), and the basis of some historic coroutine hacks.)

~~~
JoshTriplett
It's not unreasonable to think of it as "sideways", though. Because the only
other interesting meaning of "sideways" is "into another part of the same
function", and that's covered by a simple goto, without even using
setjmp/longjmp.

~~~
kazinator
It's covered by the GNU C computed goto, maybe, not the static ISO C goto.

longjmp is a computed goto; it goes wherever the jmp_buf's run-time contents
indicate.

This is not only academic; this use case occurs in exception handling based on
setjmp/longjmp, when a "finally" type cleanup block catches an exception, and
the throw originates from the same function (the "finally" block's
corresponding "try").

