114 lines
5.0 KiB
Markdown
114 lines
5.0 KiB
Markdown
---
|
|
created_at: '2015-09-14T07:50:46.000Z'
|
|
title: Call/cc for C programmers (2010)
|
|
url: http://community.schemewiki.org/?call-with-current-continuation-for-C-programmers
|
|
author: fao_
|
|
points: 66
|
|
story_text:
|
|
comment_text:
|
|
num_comments: 16
|
|
story_id:
|
|
story_title:
|
|
story_url:
|
|
parent_id:
|
|
created_at_i: 1442217046
|
|
_tags:
|
|
- story
|
|
- author_fao_
|
|
- story_10214012
|
|
objectID: '10214012'
|
|
year: 2010
|
|
|
|
---
|
|
If you're a C programmer then you've probably read the various
|
|
introductions and tutorials on
|
|
[call-with-current-continuation](/?call-with-current-continuation)
|
|
(`call/cc`) and come out not much wiser about what it all really means.
|
|
Here's the secret: it's `setjmp`/`longjmp`.
|
|
|
|
But on no account say that to any Scheme programmers you know, it'll
|
|
send them into paroxysms of rage as they tell you you don't know what
|
|
you're talking about. Oh, and if you're a Scheme programmer who's
|
|
accidentally stumbled on this page then please, please, stop reading
|
|
now, for the sake of your blood pressure. (Or in any case remember this
|
|
page is for refugee C programmers, not for you.)
|
|
|
|
## C versus Scheme
|
|
|
|
Basically `call/cc` is like `setjmp`. It records a dynamic point in the
|
|
program execution, one which you can return to, ie. jump to, later if
|
|
you want. `setjmp` does its thing by filling in a `jmp_buf` you supply,
|
|
but `call/cc` creates a continuation object and calls your nominated bit
|
|
of code with it.
|
|
|
|
The way `call/cc` calls a function to give you the continuation can be
|
|
confusing. Essentially it's just a clean way to get the continuation to
|
|
you and keep out of the way of subsequent jumps back to the saved point.
|
|
It's typical to do no more that save away the continuation object
|
|
|
|
In the name "`call-with-current-continuation`", "`call`" refers to the
|
|
way a function is called to hand over the continuation. Don't be
|
|
confused by the fact the continuation object is later invoked by calling
|
|
it, that's entirely separate.
|
|
|
|
Once you've got a continuation object, calling it as say `(cont 123)` is
|
|
like a C code `longjmp(jbuf, 123)`. It's a jump back to the original
|
|
`call/cc` point, and the return value from the `call/cc` is the 123 in
|
|
the invoking call, just like `setjmp` returns the value passed to
|
|
`longjmp`. In Scheme of course any object can be "returned" in this way
|
|
(and even values[?](/?p=values&c=e) for more than one object), not just
|
|
a single int.
|
|
|
|
This "multiple returning" by the `call/cc` shouldn't be any more
|
|
confusing that the multiple returning of a `setjmp`. However because
|
|
Scheme is more flexible it's easy to be creative with what's returned
|
|
and when and why, to create confusion where it shouldn't exist. The key
|
|
idea though remains a dynamic goto with a value passed.
|
|
|
|
In C it's only possible to jump up the stack, to a point in the current
|
|
function or a higher calling function. But in Scheme you can jump back
|
|
down as well, or even "sideways". To do this `call/cc` effectively saves
|
|
the whole stack. In fact in some Scheme implementations (for example
|
|
[Guile](/?Guile)) a block copy of the stack is exactly how it's done.
|
|
|
|
This downwards/sideways jumping is a powerful feature. Genuine
|
|
co-routines can be implemented. Or a long-running "job" can be suspended
|
|
and resumed periodically, even becoming a cooperative (and portable)
|
|
multi-tasking system. Such things are not really possible (or only with
|
|
some difficulty) in C with `setjmp`/`longjmp`.
|
|
|
|
An important note should be added about the way variables are saved in a
|
|
continuation. In C if you were to copy the stack then you copy the
|
|
values in local variables (automatics), and restoring would put back the
|
|
values copied. But effectively in Scheme it's just "what variables are
|
|
visible" which is saved and restored, the actual locations holding the
|
|
values are separate. So if you have a continuation, jump into it and
|
|
change some values, and jump in again later then the changes you made
|
|
are still there. This is much the same as in the notion of a
|
|
[closure](/?closure): the variables visible to it keep their values
|
|
between invocations.
|
|
|
|
## Not a goto
|
|
|
|
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.
|
|
|
|
A continuation represents the "what thing(s) to do next" for a program
|
|
and execution proceeds by manipulating that. A function call expands the
|
|
continuation with the body of the function (with its parameters bound to
|
|
the called values). A return reduces it back to the receiver of the
|
|
result in question, etc.
|
|
|
|
This is an abstract idea about what program execution means, but in fact
|
|
various Scheme implementations do it exactly this way, using the heap to
|
|
record the current continuation. At its simplest the current
|
|
continuation in such systems is like a list grown by [cons](/?cons)ing
|
|
onto the front, and reduced by [cdr](/?cdr)ing down. In such a system
|
|
capturing a continuation is merely saving a reference to the list, and
|
|
jumping to it just plugs that back in as the current "what to do". (And
|
|
it can be seen that method is fast, where a stack copy is slow, though
|
|
both have advantages.)
|
|
|
|
[category-learning-scheme](/?category-learning-scheme)
|