Call/cc for C programmers (2010)
community.schemewiki.org
community.schemewiki.org
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...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.
http://docs.racket-lang.org/web-server/stateless.html#%28par...
http://cs.brown.edu/~sk/Publications/Papers/Published/pcmkf-...
I might remember wrong, but I recall setjmp/longjmp having constraints on when they're useable - your "up, down sideways..."
And given that, perhaps pthreads are more civilized.
A version that would print forever: (let ((_ (display ...))) (saved-continuation "everyone"))
Some people have suggested removing call/cc from Scheme. I don't know if they want to put delimited continuations in its stead. See http://okmij.org/ftp/continuations/against-callcc.html
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.
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).
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.
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").