用 C 语言开发 WSTP (Mac OS X)

本文描述了在 Mac OS X 系统中如何编译和运行用 C 语言编写的 Wolfram Symbolic Transfer Protocol (WSTP) 程序. (WSTP 和外部程序的通讯描述了如何用 Wolfram 语言和 C 语言编写 WSTP 程序.)
本文并不教你如何使用编译器和其它开发工具,也不会教您如何用 C 语言编程. 如果您有任何开发或运行 WSTP 程序的问题,请参阅本文末尾的疑难解答章节.
本文所叙述的大部分内容是专门面向 Unix 并适用于所有被支持的 Unix 平台. 然而,有些信息是专门针对 Mac OS X 机器的. 若想学习如何在其它平台上编译和运行用 WSTP 程序,请参阅相关平台的开发指南.
支持的开发平台
Mac OS X 的 WSTP 开发工具包(WSDK)中的 wstp.framework 共享库可以用于 Mac OS X 10.9.0 及更新版本,和 Xcode 6.x 及更新版本. 该框架包括对 32 位 x86 和 64 位 x86 架构的通用二进制支持.
安装 WSTP 组件
WSTP 开发工具包位于 Wolfram 系统目录中的 $InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/MacOSX-x86-64 目录下.

建议安装

CompilerAdditions 安装

构建 WSTP 程序所需要的 WSTP 组件已安装在 Wolfram 系统包中. 使用这些组件的一种方法是让它们保留在 Mathematica.app 目录中,当调用编译器时指定它们的完整路径. 该方法列在构建 章节中的范例makefiles中.
另一种方法是把这些组件 (wstp.h、libWSTPi4.a、wstp.framework) 复制到编译器能自动搜索类似文件的目录中. 对于 wstp.h 和 libWSTPi4.a 来说,这些目录一般分别是 /usr/include 或 /usr/lib,而对 wstp.framework 来说则是 /Library/Frameworks 或 ~/Library/Frameworks. 在许多系统中,不是所有用户都对这些目录有写的权限.

WSTPExamples 安装

把 WSTPExamples 目录复制到主目录.

WSTP 框架组件

下面是 WSDK 中每个文件或目录的说明.

CompilerAdditions 目录

wstp.h
wstp.h 是一个必须包含在 C 和 C++ 源文件中的头文件. 它应该放在你的编译器可以找到的地方. 你可以把它复制到和源文件同样的目录下,或复制到和标准头文件同样的目录下,或者不用管它,但是把 WSTP 目录添加到头文件的搜索路径中.
libWSTPi4.a
libWSTPi4.a 是一个包含所有 WSTP 函数的静态库. 你的项目应该包含它. 你可以把该库复制到与源文件同样的目录下或不做任何改变,但是把 WSTP 目录添加到库文件的搜索路径中.
wstp.framework
wstp.framework 是包含所有 WSTP 函数的动态库框架. 当你想构建动态链接 WSTP 库的程序时,需使用该框架. 你可以把该库复制到与源文件同样的目录下或不做任何改变,但是把 WSTP 目录添加到框架的搜索路径中.
wsprep
wsprep 是一个应用程序,通过处理模板文件自动编写 WSTP 程序. 把该应用程序复制到你的项目目录中或创建一个别名会方便应用.
wscc
wscc 是一个预处理和编译 WSTP 源文件的脚本.

WSTPExamples 目录

该目录包含一些非常简单的 WSTP 程序源代码. 通过使用源代码,你可以学习如何构建和运行 WSTP 程序,而无需自己编写任何代码.

PrebuiltExamples 目录

该目录包含范例程序的预建版本. 运行 WSTP 程序描述了如何运行两个这样的程序. 构建 WSTP 程序描述了如何使用 WSTPExamples 目录中的源代码构建自己的程序.
构建 WSTP 程序
构建 WSTP 程序的一般过程是在任何调用 WSTP 函数的 C 或 C++ 源文件中包含 wstp.h,编译源文件,然后把 libWSTPi4.a 库或 wstp.framework 以及应用程序需要的任何其他标准库与结果对象代码相链接. 如果你的应用程序使用 WSTP 模板机制,那么你的模板文件必须首先使用 wsprep 处理成 C 源文件.

使用 WSTP 模板文件

如果你的程序使用WSTP 和外部程序通讯中描述的 WSTP 模板机制,你必须同时使用 wsprep 应用程序预处理包含模板项的源文件.(模板项是包含模板关键字的行序列,每一项定义当调用相关 C 函数时的 Wolfram 语言函数.)当 wsprep 处理这类文件时,它会把模板项转换成 C 函数,而无需改变其他文本,并使用 WSTP 应用远程过程调用机制编写其他 C 函数. 结果是可用于编译的 C 源文件.
例如,命令
wsprep addtwo.tm -o addtwotm.c
会从模板项中产生一个 C 源文件 addtwotm.c,其他文本保留在 addtwo.tm. 然后你可以使用 C 编译器编译输出文件. 如果你使用 make 工具构建你的程序,你可以添加类似下面的规则至你的制作文件(makefile).
addtwotm.c  :  addtwo.tm
wsprep addtwo.tm -o addtwotm.c

从命令行构建 WSTP 程序

以下是一个构建 WSDK 中样本程序需要的样本 makefile,包括 addtwofactor. 要构建样本程序,例如 addtwo,在 WSTPExamples 目录中计算以下命令.

使用 Makefile

# This makefile can be used to build all or some of the sample
# programs. To build all of them, use the command
# 'make all'. To build one, say addtwo, use the command
# 'make addtwo'.

WSLINKDIR = /Applications/Mathematica.app/Contents/SystemFiles/Links/WSTP/DeveloperKit
SYS = MacOSX-x86-64
CADDSDIR = ${WSLINKDIR}/${SYS}/CompilerAdditions

INCDIR = ${CADDSDIR}
LIBDIR = ${CADDSDIR}

WSPREP = ${CADDSDIR}/wsprep

EXTRA_LIBS = -lc++ -framework Foundation

WSTP_LIB = -lWSTPi4

all : addtwo bitops counter factor factor2 factor3 quotient reverse sumalist

addtwo : addtwotm.o addtwo.o
    ${CC} -I${INCDIR} addtwotm.o addtwo.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

bitops : bitopstm.o bitops.o
    ${CC} -I${INCDIR} bitopstm.o bitops.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

counter : countertm.o
    ${CC} -I${INCDIR} countertm.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

factor : factor.o
    ${CC} -I${INCDIR} factor.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

factor2 : factor2.o
    ${CC} -I${INCDIR} factor2.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

factor3 : factor3.o
    ${CC} -I${INCDIR} factor3.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

quotient : quotient.o
    ${CC} -I${INCDIR} quotient.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

reverse : reversetm.o
    ${CC} -I${INCDIR} reversetm.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

sumalist : sumalisttm.o sumalist.o
    ${CC} -I${INCDIR} sumalisttm.o sumalist.o -L${LIBDIR} ${WSTP_LIB} ${EXTRA_LIBS} -o $@

.c.o :
    ${CC} -c -I${INCDIR} $<

addtwotm.c : addtwo.tm
    ${WSPREP} $? -o $@

bitopstm.c : bitops.tm
    ${WSPREP} $? -o $@

countertm.c : counter.tm
    ${WSPREP} $? -o $@

reversetm.c : reverse.tm
    ${WSPREP} $? -o $@

sumalisttm.c : sumalist.tm
    ${WSPREP} $? -o $@

库依赖

当从命令行中构建 WSTP 程序时,开发人员必须和 libc++ 以及 Foundation 框架相链接.

用 Xcode 构建 Mac OS X WSTP 程序

Xcode 6.x+

创建一个 addtwo 项目

创建一个用于编辑、构建和调试 addtwo 的项目:

1.  启动 Xcode

2.  File 菜单,选择 New Project.

会出现 Choose a template for your new project 对话框.

3.  OS X 下点击 Application. 然后点击 Command Line Tool 图标. 点击 Next.

会出现 Choose options for your new project 对话框.

4.  Product Name 文本框中输入 addtwo. 在 Type 的下拉菜单中,选择 C. 不选 Use Automatic Reference Counting 复选框. 点击 Next.

会出现 Choose Folder 对话框.

5.  在你的文件系统为 addtwo 项目创建一个目录. 根据你的需要确定是否为 addtwo 项目创建 git 仓库来选择 Source Control 复选框. 点击 Create.

Xcode 会打开 addtwo 工作表.

6.  把源文件复制到项目目录.

启动终端应用(/Applications/Terminal.app)并改变为(cd)安装 Wolfram 系统的目录. 然后,cd$InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/MacOSX-x86-64/WSTPExamples.

使用复制(cp)命令把 addtwo 源文件复制到你在步骤 4 中选择的项目目录中.

7.  在模板文件中运行 wsprep.

把目录变为项目目录.

使用 wsprep 产生一个源文件.

8.  往项目中添加文件.

9.  从项目中去除 main.c.

右击文件 main.c 并选择 Delete. 在下拉菜单中选择 Move to Trash 按钮.

10.  构建项目.

Project 菜单,选择 Build.

创建一个 Factor 项目

创建一个可用于编辑、构建和调试 factor 的项目:

1.  启动 Xcode

2.  File 菜单中,选择 New Project.

会出现 Choose a template for your new project 对话框.

3.  OS X 部分,点击 Application. 然后点击 Command Line Tool 图标. 点击 Next.

会出现 Choose options for your new project 对话框.

4.  Product Name 文本框中输入factor. 从 Type 下拉菜单中选择 C. 不选 Use Automatic Reference Counting 复选框. 点击 Next.

会打开 Choose Folder 对话框.

5.  在你的文件系统为 factor 项目创建一个目录. 根据你的需要确定是否为 factor 项目创建 git 仓库来选择 Source Control 复选框. 点击 Create.

Xcode 会打开 addtwo 工作表.

6.  把源文件复制到项目目录.

启动终端应用(/Applications/Terminal.app)并改变为(cd)安装 Wolfram 系统的目录. 然后,cd$InstallationDirectory/SystemFiles/Links/WSTP/DeveloperKit/MacOSX-x86-64/WSTPExamples.

使用复制(cp)命令把 factor 源文件复制到你在步骤4中选择的项目目录中.

7.  往项目中添加文件.

8.  从项目中去除 main.c.

右击文件 main.c 并选择 Delete. 在下拉菜单中选择 Move to Trash 按钮.

9.  构建项目.

Project 菜单,选择 Build.

使用 wscc

wscc 是一个预处理和编译 WSTP 源文件的脚本. 它会预处理任何以 .tm 结尾的文件中的 WSTP模板,然后在结果的 C 源代码中调用 cc. wscc 会直接传递命令行选项和其他文件至 cc. 以下是使用 wscc 构建 addtwo 应用的命令.
wscc addtwo.tm addtwo.c -o addtwo
运行 WSTP 程序
构建 WSTP 程序中的说明描述了如何使用 WSTPExamples 目录中的源代码构建两个 WSTP 程序. 这两个程序,addtwofactor 已存在于 PrebuiltExamples 文件夹. 在构建你自己的之前,应该试着运行预建的范例以验证 WSTP 系统附件是否安装且正常工作,并学习当正确构建时这些范例会有怎样的结果. 其他说明是假设你正在使用开发程序包中的程序.

在 Wolfram 语言内核中运行预建范例

第一个范例程序,addtwo 是安装在 Wolfram 语言中的一个 WSTP 模板. 也就是说,该程序在后台运行,为 Wolfram 语言,一个或多个外部编译函数提供服务. 对于 Wolfram 语言用户,这些函数显现为内置的. 为了获得该新功能,Wolfram 语言的用户必须运行 Install[] 函数. addtwo 程序使用定义 Wolfram 语言函数的模板文件 AddTwo[] 作为一个对 C 函数 addtwo()的调用. (模板机制的详细描述参见WSTP 和外部程序通讯.)该程序的源代码为:
计算下面两个单元:
查找最新可用函数的列表,计算下面单元:
以下会显示定义在文件 addtwo.tm 中的 AddTwo[] 函数的用法信息:
尝试一下:
看看如果两个机器整数的和不匹配于一个机器的整数或如果任何一个参数都不是机器整数,会出现什么情况. (2^31-1 是最大的机器整数. 如果您的编译器使用2个字节整数,那么 2^31-1 是最大的 C int.)
addtwo 程序不适于大的整数:
以下不匹配 AddTwo[_Integer,_Integer]
Install[] 调用 LinkOpen[],然后和外部程序交换信息,设置 AddTwo[] 的定义. 你无需考虑这些细节,但是如果你很好奇,可执行以下命令:
当你不再使用外部程序,执行如下命令:

从一个预建的范例中调用 Wolfram 语言内核

第二个范例,factor 是运行在后台的 Wolfram 语言内核为 factor 提供服务,内核的计算服务,Wolfram 系统的前端对用户的输入整数给出因式分解. 这些例子是假设 Wolfram 系统安装在 Applications 目录中.
通过执行以下命令,启动factor应用.
过一会会出现提示要求你输入整数. 敲入少于10位的整数,然后按下 Return 键.(其他 factor 例子会放宽对输入整数大小的限制.)
输出由 Wolfram 语言产生的质因数分解,然后 factor 关闭它与Wolfram 语言的链接.

支持的链接协议

C 函数 WSOpenArgcArgv() 和 Wolfram 语言函数 LinkOpen[] 的细节请参见WSTP 和外部程序通讯. 在 Macintosh OS X 机器中,LinkProtocol 选项的合法值为 TCPIPTCPSharedMemoryPipes 和 IntraProcess. LinkProtocol 是用于从一端到另一端传输数据的机制. SharedMemoryLinkMode->Launch 链接的默认协议. LinkMode->ListenLinkMode->Connect 链接的默认值是 SharedMemory.
请注意,TCPIPTCP 协议的链接名称是16位无符号整数. 虽然TCPIP链接的名称是整数,但是它们仍然以(数字)字符串形式赋给 WSOpenArgcArgv()LinkOpen[].
疑难解答