4.3 创建脚本语言扩展

将C/C++语言与脚本语言集成的最后一步是将你的扩展添加到脚本语言中。这里有两种主要方法去完成。比较好的技术是创建共享库构建可动态加载的扩展。另外一种方法是,将你的扩展代码添加到脚本语言的解析器中,然后重新编译脚本语言解析器。

4.3.1 共享库和动态加载

为创建动态库或DLL,你一般需要查看编译器和链接器的手册。部分平台的构建步骤一般如下:

  1. # Build a shared library for Solaris
  2. gcc -fpic -c example.c example_wrap.c -I/usr/local/include
  3. ld -G example.o example_wrap.o -o example.so
  4. # Build a shared library for Linux
  5. gcc -fpic -c example.c example_wrap.c -I/usr/local/include
  6. gcc -shared example.o example_wrap.o -o example.so

为使用你的共享库,可以简单地使用脚本语言相应的命令(load,import,use等)。它将导入你的模块,并允许你开始使用。例如:

  1. % load ./example.so
  2. % fact 4
  3. 24
  4. %

当操作C++代码时,构建共享库的过程可能更复杂点,主要是因为C++模块需要额外的代码才能正确工作。在多数机器上,你可以安装上面的过程构建共享库,但需要更改链接部分:

  1. c++ -shared example.o example_wrap.o -o example.so

4.3.2 链接共享库

当构建共享库形式的扩展时,可能它还以来你机器上的其它共享库。为了让扩展正常工作,需要在运行时找到它们。否则你可能会得到像下面这样的错误提示:

  1. >>> import graph
  2. Traceback (innermost last):
  3. File "<stdin>", line 1, in ?
  4. File "/home/sci/data1/beazley/graph/graph.py", line 2, in ?
  5. import graphc
  6. ImportError: 1101:/home/sci/data1/beazley/bin/python: rld: Fatal Error: cannot
  7. successfully map soname 'libgraph.so' under any of the filenames /usr/lib/libgraph.so:/
  8. lib/libgraph.so:/lib/cmplrs/cc/libgraph.so:/usr/lib/cmplrs/cc/libgraph.so:
  9. >>>

这个错误说明SWIG创建的扩展模块依赖libgraph.so共享库,但运行时系统找不到它。为解决这个问题,你可以使用以下几种方式:

  • 链接你的扩展库,并显式告诉链接器它需要的共享库的位置。多数时间,可以通过制定特殊的链接器标志如-R 或-rpath等。这些都不时标注实现,因此你需要阅读链接器的man手册,查找如何设置共享库的搜索路径。
  • 将共享库和可执行文件放到共同的目录。这种技术在非UNIX平台上正确工作的必须方法。
  • 在Python程序运行前,设置UNIX环境变量LD_LIBRARY_PATH为共享库存放的目录。尽管这是一种简单的解决方案,但不推荐。考虑设置链接器的路径标志。

4.3.3 静态库

为静态链接,需要将扩展和脚本语言的解析器一起重新编译。这个过程一般包括编译一小段main程序,添加你的自定义命令到解析器,然后运行解析器。这种方法可以让你生成一个自定义的脚本语言解析器。

尽管所有的平台都支持静态链接,但这不是创建脚本语言扩展最好的技术。事实上,实际不推荐这么做,应该考虑使用共享库的方式代替。