4 外部接口

4.1 子进程通信

4.2 导入Scheme

4.3 从Scheme导出

4.4 延续和外部引用

4.5 外部数据

4.6 访问外部过程

可以通过以下几种方式获得外部过程:

  • 可以使用加载动态链接库过程 load-shared-object 加载外部过程。
  • 一个新的Chez Scheme映像可以用加载外部代码构建的方式链接。Sforeign_symbol Sregister_symbol 参见4.8
  • 其他条目可以动态加载或以其他方式由外部代码获得。通常也使用Sforeign_symbol Sregister_symbol进行注册。
  • 入口地址,比如函数指针,可以被传递给Scheme,并用作外部过程表达式中入口语句的值。即使没有按照名称注册,也可以使用外部入口点。
过程:(foreign-entry? entry-name)
返回:如果entry-name存在,返回#t,否则#f
库:(chezscheme)

entry-name 必须是 string,可用于确定是否存在外部程序的过程。

以下示例假定定义strlen的库已经通过load-shared-object加载,或者strlen已经通过本节中介绍的其他方法注册。

foreign-entry? "strlen")#  => t 
    ((foreign-procedure "strlen" 
    (string) size_t)
 "hey!")  => 4
过程:(foreign-entry entry-name)
返回:以integer返回entry-name名称的address
库:(chezscheme)

入口名称必须是一个命名现有外来入口点的字符串。

以下示例假定定义strlen的库已经通过load-shared-object加载,或者strlen已经通过本节中介绍的其他方法注册。

(let ([addr (foreign-entry "strlen")])
    (and (integer? addr) (exact? addr))) => #t 

(define-ftype strlen-type (function (string) size_t))
(define strlen
    (ftype-ref strlen-type ()
    (make-ftype-pointer strlen-type "strlen")))
(strlen "hey!") => 4
过程:(foreign-address-name address)
返回:address对应的entry-name。若不存在,返回#f
库:(chezscheme)

以下示例假定定义strlen的库已经通过load-shared-object加载,或者strlen已经通过本节中介绍的其他方法注册。

(foreign-address-name (foreign-entry "strlen")) => "strlen"
过程:(load-shared-object path)
返回:unspecified 
库:(chezscheme)

path必须是一个字符串。load-shared-object加载由path指定的动态链接库。动态链接库可能是系统库或从普通C程序创建的文件。动态链接库中的所有外部符号以及与shared-object链接的其他动态链接库中可用的外部符号都可用作外部条目。

在Chez Scheme运行的大多数平台上都支持这个过程。

如果path不以"."或者"/"开始,shared-object将在系统默认的环境变量中进行搜索。

在大多数Unix系统上,load-shared-object基于系统例程dlopen。在Windows下,load-shared-object基于LoadLibrary。有关这些例程的文档,请参阅C编译器和加载器,以获取有关查找和构建动态链接库的精确规则。

load-shared-object可以用来访问内置的C库函数,比如getenv。动态链接库的名称因系统而异,在Linux系统上:

(load-shared-object "libc.so.6")

在Solaris,OpenSolaris,FreeBSD,NetBSD和OpenBSD系统上:

(load-shared-object "libc.so")

在MacOS X系统上:

(load-shared-object "libc.dylib")

在Windows上:

(load-shared-object "crtdll.dll")

一旦C库被加载,getenv应该可以作为一个外部入口:

(foreign-entry? "getenv") => #t

可以这样定义和调用等效的Scheme过程:

(define getenv
    (foreign-procedure "getenv"
        (string)
        string))
(getenv "HOME") => "/home/elmer/fudd"
(getenv "home") => #f

load-shared-object也可以用来访问用户创建的库,

假设C文件"env.c"包含

int even(n) int n; { return n == 0 || odd(n - 1); }

C文件"odd.c"包含

int odd(n) int n; { return n != 0 && even(n - 1); }

这些文件必须被编译并链接到动态链接库才能被加载。其过程取决于系统:

在Linux,FreeBSD,OpenBSD和OpenSolaris上:

(system "cc -fPIC -shared -o evenodd.so even.c odd.c")

根据主机配置的不同,可能需要 -m32 或 -m64 选项来指定32位或64位编译。

在MacOS X(Intel或PowerPC)系统上:

(system "cc -dynamiclib -o evenodd.so even.c odd.c")

根据主机配置的不同,可能需要 -m32 或 -m64 选项来指定32位或64位编译。

在32位Sparc Solaris上:

(system "cc -KPIC -G -o evenodd.so even.c odd.c")

在64位Sparc Solaris上:

(system "cc -xarch=v9 -KPIC -G -o evenodd.so even.c odd.c")

在Windows上,我们构建一个DLL(动态链接库)文件。为了使编译器生成适当的入口点,我们改变even.c来读取

#ifdef WIN32
#define EXPORT extern __declspec (dllexport)
#else
#define EXPORT extern
#endif 

EXPORT int even(n) int n; { return n == 0 || odd(n - 1); }

和odd.c读取

#ifdef WIN32
#define EXPORT extern __declspec (dllexport)
#else
#define EXPORT extern
#endif 

EXPORT int odd(n) int n; { return n != 0 && even(n - 1); }

然后,我们可以按如下所示构建DLL,并为其提供扩展名“.so”而不是“.dll”,以便与其他系统保持一致。

(system "cl -c -DWIN32 even.c")
(system "cl -c -DWIN32 odd.c")
(system "link -dll -out:evenodd.so even.obj odd.obj")

生成的“.so”文件可以加载到Scheme中,even和odd可以作为外部过程使用:

(load-shared-object "./evenodd.so")
(let ([odd (foreign-procedure "odd"
             (integer-32) boolean)]
      [even (foreign-procedure "even"
              (integer-32) boolean)])
  (list (even 100) (odd 100))) => (#t #f)

文件名以“./evenodd.so”定义,而不是简单的“evenodd.so”,因为有些系统在不包含当前目录的标准系统目录集中查找共享库。

过程:(remove-foreign-entry entry-name) 
返回:unspecified 
库: (chezscheme)

remove-foreign-entry将访问由entry-name指定的入口。如果目标不存在,就会引发异常。可在外部接口建立后用remove-foreign-entry而不影响之前由foreign-procedure建立的接口访问。

使用remove-foreign-entry可删除使用Sforeign_symbol和Sregister_symbol注册的条目,但不能删除由调用load-shared-object而创建的条目。

4.7 使用其他的编程语言

尽管Chez Scheme外部过程接口主要面向C中定义的过程或C库中可用的过程,但也可以调用其他语言中遵循C调用约定的过程。一个难点的来源可能是名字的解释。基于Unix的C编译器通常会在外部名称前加一个下划线,外部接口将尝试以与主机C编译器一致的方式解释条目名称。

偶尔,如汇编代码文件,这个条目的名称可能不以被期望的方式解释。通过在条目名称前添加一个“=”字符来防止。

例如,加载包含过程“foo”的程序集文件后,可能会有

(foreign-entry?“foo”)#  => f 
(foreign-entry?“= foo”)#  => t

4.8 C库过程

4.9 示例:套接字操作

results matching ""

    No results matching ""