`
libudi
  • 浏览: 35086 次
  • 性别: Icon_minigender_1
  • 来自: 郑州
社区版块
存档分类
最新评论

示例分析CALL/CC

阅读更多
说明:此文内容纯属个人看法,无所谓对错,欢迎拍砖!

call/cc(call-with-current-continuation)是函数编程的顶级概念,虽然我们用到的场合不是太多,但重要性非比寻常。

call/cc的魔力在于迫使运行的程序通过时光隧道返回到从前的某个时刻。

从返回的方式看我们可以有多种理解,下面是最常见的两种:

1、逐步退回:一步就是一次函数调用。执行call/cc时,程序自动执行函数返回操作(RET),直到返回的位置与call/cc的要求一致。

2、直接跳转:程序的指令系统直接切换到call/cc的位置,被抛掉的运行节点和对象留给GC处理。

命令式语言(如C/C++/PASCAL)可以很方便的实现第一种返回方式,比如使用异常和长跳转,但由于基于栈运行和缺乏GC支持,很难实现第二种返回方式。我们不在这里讨论语言的分类问题,而是通过一个例子,凭想象来推测一下call/cc背后都发生了些什么。

void b(function cc)
{
  = "b";
  cc(); // 返回a()中创建cc对象后的下一条指令位置
}

void a()
{
  callcc cc() { = "a" }; // 创建cc对象,这里是个函数
  b(cc);
}


现在运行函数a()并登记其运行过程:

a()开始执行。
|__创建call/cc函数cc()。
|__跳过cc()的返回执行代码。
|__将cc传递到b()函数中加以执行。
...b()开始执行
...|__打印字符'b'
...|__执行cc()。
......cc()开始执行
......|___中断b()回到a(),指令系统指向cc()的返回执行代码。
|__打印字符'a'
|__回到步骤4。


cc在第9步直接中断b()的运行回到a()并将指令系统指向cc()的返回执行代码,这一步是怎么发生的呢?

一、可以确定cc至少携带了以下两个信息:

1、a()的运行时信息(比如栈基址)。
2、cc返回执行代码的地址。

我们用结构ccrec来描述它们:

// call/cc 信息结构定义
struct ccrec {
  void * addr; // 函数运行时信息的地址
  unsigned long next; // 下一条指令的位置
}


指令系统可以假定为以下设计:

// 指令系统结构定义
struct isrec {
  struct ccrec current; // 当前运行时信息
  struct isrec *prior; // 上一级调用的指令系统结构
}

struct isrec *current; // 当前指令系统结构

void *get_call_base() // 提取当前运行时信息
{
  return current->current.addr;
}

unsigned long get_next_instruction() // 返回下一条指令的地址
{
  return current->current.next;
}

void set_next_instruction(unsigned long next) // 设置下一条指定的地址
{
  current->current.next = next;
}


二、callcc创建cc对象后在其内部保存以上两条信息:

// callcc 指令的处理过程
void * cc = create_function(); // 创建 cc 函数
struct ccrec * rec = create_new_ccrec(); 
rec->addr = get_call_base(); // 提取当前函数运行时信息
rec->next = get_next_instruction(); // 下一条即将执行的的指令
save_ccrec(cc, rec); // 保存信息到cc对象


三、cc执行时重置了指令系统的状态:

// 提取call/cc返回信息

struct ccrec * rec = load_ccrec(cc);

// 执行返回操作

while (rec->addr != get_call_base())
  exec_return(); //==> 它做了什么?

// 设置下一条指令

set_next_instruction(rec->next);


函数exec_return()不应该太复杂,大致的是如下的代码:

// 函数返回操作函数
void exec_return()
{
  // 1. 加锁休眠垃圾回收并返回当前指令系统结构
  struct isrec * curr = begin_return(); 

  // 2. 将当前运行时信息登记到垃圾回收系统
  gc_notify(curr);
 
  // 3. 设置指令系统,返回上一级调用
  current = curr->prior; 

  // 4. 完成返回操作后开锁,重新激活垃圾系统
  end_return(); 
}


这样的想象和推测单从理解上说应该是完备的,基于这个理解,我们试着做些更深层次的发挥。
分享到:
评论

相关推荐

    opc ua客户端C C++示例源码.zip

    【程序老媛出品,必属精品...资源名:opc ua客户端C C++示例源码.zip 资源类型:程序源代码 源码说明: 基于C C++写的OPC UA 客户端程序源码 包含完整源码和注释 很适合借鉴学习 适合人群:新手及有一定经验的开发人员

    fortran-idl-c-interoperability:这是一组代码示例,展示了如何将 Fortran 9x2003 代码合并到 CC++ 和 Exelis IDL 中

    ISO C/C++ 和 Fortran 互操作性示例这是一组代码示例,展示了如何将 Fortran 9x/2003 代码合并到 C/C++ 和 Exelis IDL 中。这里有什么?...idl_call_fortran 此处的示例演示了如何从 IDL 调用已“C 有界

    cate:基于延续的异步任务执行器

    控制流的中间状态(称为Context )可以随时保存和恢复(如 call/cc),这也使得调用基于传统异步接口的回调变得更加容易。 大多数原始任务都是无状态的,这意味着组合任务可以在大多数情况下重用。 凯特很容易...

    Fanuc焊接机器人编程实例.txt

    /PROG PIPE_2SS1CC /ATTR OWNER = MNEDITOR; COMMENT = "START_STOP"; PROG_SIZE = 8121; CREATE = DATE 10-11-25 TIME 14:22:04; MODIFIED = DATE 10-11-25 TIME 16:33:18; FILE_NAME = PIPE_2SS; VERSION = 0; ...

    libhmemory:hmemory 是 cc++ 程序的轻量级内存错误检测器,专为嵌入式系统设计

    记忆hmemory 是 c/c++ 程序的内存错误检测器。1. 概述hmemory 是用于 c/c++ 程序的轻量级内存错误检测器,专为嵌入式系统... HMEMORY_ENABLE_CALLSTACK 默认 0 启用/禁用报告错误时的调用跟踪信息,有用但取决于libbd

    js使用小技巧

    Javascript小技巧一箩筐 事件源对象 event.srcElement.tagName event.srcElement.type ... 捕获释放 event.srcElement.setCapture();...event.srcElement.releaseCapture();... 根据鼠标获得元素: document....

    出现问题a is defined高手帮忙

    <!... 便民设施系统</title> ... charset=gbk"/> <link rel="stylesheet" type="text/css" href="style.css"></link> ... key=ABQIAAAAzr2EBOXUKnm_jVnk0OJI7xSosDVG8KKPE1-m51RBrvYughuyMxQ- ... type="text/javascript"></...

    cscout:C代码重构浏览器

    有关更多详细信息,示例和文档,请访问项目的。构建,测试,安装,使用CScout已在GNU / Linux(Debian jessie),Apple OS X(El Capitan),FreeBSD(11.0)和Cygwin上进行编译和测试。 为了构建和使用CScout,您...

    product-api

    产品API 这是一个基于的微服务项目。...迎宾员:带有hello和welcome操作的示例服务。 有用的链接 分子网站: : 分子文件: : NPM脚本 npm run dev :启动开发模式(通过热重载和REPL在本地加载所有服务) npm ru

    javascript语言参考+教程 CHM

    FileSystemObject 示例代码; Scripting 运行时库参考; 脚本运行时方法; Add 方法 (Dictionary); Add 方法 (Folders); BuildPath 方法; Close 方法; Copy 方法; CopyFile 方法; CopyFolder 方法; ...

Global site tag (gtag.js) - Google Analytics