E.8.5 模块中的非本地退出

Emacs Lisp 支持非本地退出,借此可以将程序控制从程序中的某个点转移到另一个遥远的点。See 非局部退出。因此,由你的模块调用的 Lisp 函数可能会通过调用 signalthrow 进行非本地退出,你的模块函数必须正确处理此类非本地退出。需要进行此类处理的原因是,C 程序在这些情况下不会自动释放资源并执行其他清理工作;你的模块代码必须亲自完成这些工作。模块 API 提供了相应的工具,如本小节所述。这些工具从 Emacs 25 版本开始普遍可用;后续版本中加入的工具会明确标注其首次成为 API 一部分的 Emacs 版本。

当模块函数调用的某些 Lisp 代码触发错误或抛出异常时,非本地退出会被捕获,待处理的退出及其相关数据会存储在环境中。每当环境中存在待处理的非本地退出时,使用该环境指针调用的任何模块 API 函数都会立即返回,不执行任何处理(non_local_exit_checknon_local_exit_getnon_local_exit_clear 函数除外)。如果你的模块函数随后不做任何处理并返回给 Emacs,待处理的非本地退出将导致 Emacs 执行相应操作:触发错误或跳转到对应的 catch

因此,在模块函数中对非本地退出最简单的 “处理(handling)” 是不做特殊处理,让其余代码照常运行,仿佛什么都没发生。然而,这可能导致两类问题:

因此,我们建议你的模块函数使用下文描述的函数,检查非本地退出条件并从中恢复。

Function: enum emacs_funcall_exit non_local_exit_check (emacs_env *env)

该函数返回存储在 env 中的非本地退出条件的类型。可能的值包括:

emacs_funcall_exit_return

最后一个 API 函数正常退出。

emacs_funcall_exit_signal

最后一个 API 函数触发了错误。

emacs_funcall_exit_throw

最后一个 API 函数通过 throw 退出。

Function: enum emacs_funcall_exit non_local_exit_get (emacs_env *env, emacs_value *symbol, emacs_value *data)

该函数与 non_local_exit_check 类似,返回存储在 env 中的非本地退出条件的类型,但同时会返回关于非本地退出的完整信息(如果存在)。如果返回值为 emacs_funcall_exit_signal,函数会将错误符号存入 *symbol,将错误数据存入 *data(see 如何发出错误信号)。如果返回值为 emacs_funcall_exit_throw,函数会将 catch 标签符号存入 *symbol,将 throw 的值存入 *data。当返回值为 emacs_funcall_exit_return 时,函数不会向这些参数指向的内存中存入任何内容。

你应该在关键位置检查非本地退出条件:在分配某些资源之前,或在分配可能需要释放的资源之后,或在故障意味着无法或不可行继续处理的位置。

一旦你的模块函数检测到有待处理的非本地退出,它可以选择返回 Emacs(执行必要的本地清理后),或尝试从非本地退出中恢复。以下 API 函数将有助于完成这些任务。

Function: void non_local_exit_clear (emacs_env *env)

该函数清除 env 中待处理的非本地退出条件和数据。调用后,模块 API 函数将恢复正常工作。如果你的模块函数能够从其调用的 Lisp 函数的非本地退出中恢复并继续运行,请使用此函数;此外,在调用以下两个函数(或任何其他 API 函数,如果希望它们在有待处理的非本地退出时执行预期的处理)之前,也应调用此函数。

Function: void non_local_exit_throw (emacs_env *env, emacs_value tag, emacs_value value)

该函数跳转到由 tag 表示的 Lisp catch 符号,并将 value 作为返回值传递给它。你的模块函数通常应在调用此函数后尽快返回。此函数的一个用途是重新抛出从被调用的 API 或 Lisp 函数之一产生的非本地退出。

Function: void non_local_exit_signal (emacs_env *env, emacs_value symbol, emacs_value data)

该函数使用指定的错误数据 data,触发由错误符号 symbol 表示的错误。模块函数通常应在调用此函数后尽快返回。此函数可能很有用,例如,用于从模块函数向 Emacs 触发错误。