5.1 运行SWIG

可使用swig命令运行SWIG,指定选项和文件名:

  1. swig [ options ] filename

这里,filename是SWIG接口文件或C/C++的头文件。下面是一组可用的选项子集。每种目标语言还定义了额外的选项。通过键入swig -help或 swig -lang -help可以获取全部选项。

  1. -allegrocl Generate ALLEGROCL wrappers
  2. -chicken Generate CHICKEN wrappers
  3. -clisp Generate CLISP wrappers
  4. -cffi Generate CFFI wrappers
  5. -csharp Generate C# wrappers
  6. -go Generate Go wrappers
  7. -guile Generate Guile wrappers
  8. -java Generate Java wrappers
  9. -lua Generate Lua wrappers
  10. -modula3 Generate Modula 3 wrappers
  11. -mzscheme Generate Mzscheme wrappers
  12. -ocaml Generate Ocaml wrappers
  13. -perl Generate Perl wrappers
  14. -php Generate PHP wrappers
  15. -pike Generate Pike wrappers
  16. -python Generate Python wrappers
  17. -r Generate R (aka GNU S) wrappers
  18. -ruby Generate Ruby wrappers
  19. -sexp Generate Lisp S-Expressions wrappers
  20. -tcl Generate Tcl wrappers
  21. -uffi Generate Common Lisp / UFFI wrappers
  22. -xml Generate XML wrappers
  23. -c++ Enable C++ parsing
  24. -cppext ext Change file extension of C++ generated files to ext (default is cxx, except for PHP which -Dsymbol Define a preprocessor symbol
  25. -Fstandard Display error/warning messages in commonly used format
  26. -Fmicrosoft Display error/warning messages in Microsoft format
  27. -help Display all options
  28. -Idir Add a directory to the file include path
  29. -lfile Include a SWIG library file.
  30. -module name Set the name of the SWIG module
  31. -o outfile Set name of C/C++ output file to <outfile>
  32. -oh headfile Set name of C++ output header file for directors to <headfile>
  33. -outcurrentdir Set default output dir to current dir instead of input file's path
  34. -outdir dir Set language specific files output directory
  35. -pcreversion Display PCRE version information
  36. -swiglib Show location of SWIG library
  37. -version Show SWIG version number

5.1.1 输入格式

SWIG的输入可以使包含ANSI C/C++的声明或包含特殊的SWIG指令。多数情况下,它是一个特殊的SWIG接口文件,文件后缀为.i或.swg。某些情况下,SWIG可以直接作用域原始的头文件或源文件上。但是,这种情况不常用,后面会解释为什么不要这么做。

SWIG接口文件常见格式如下:

  1. %module mymodule
  2. %{
  3. #include "myheader.h"
  4. %}
  5. // Now list ANSI C/C++ declarations
  6. int foo;
  7. int bar(int x);
  8. ...

%module指令指定模块的名字。模块会在模块介绍节介绍。

%{...%}块之间的一切将原封不动的拷贝到SWIG结果生成的包装文件中。本节它主要用于包含头文件和用于使包装代码编译通过的其他声明。需要重点强调的是,需要你在SWIG的输入文件中包含了声明,这些声明不会自动出现在生成的包装代码中。因此,你需要确定在%{...%}中包含了合适的头文件。需要注意的是%{...%}包含的代码,SWIG是不会解释的。%{...%}的语法和语义类似yacc和bison工具中输入为文件中的声明。

5.1.2 SWIG输出

SWIG的输出是C/C++文件,这个文件包含了用于创建扩展的所有的包装代码。根据目标语言的不同,SWIG可能还会产生其他额外的文件。默认情况下,file.i或转换输入为file_wrap.c或file_wrap.cpp(依赖于是否指定了-C++选项)。使用-o选项可以改变输出文件的名字。在某些情况下,编译器使用文件后缀决定源代码使用的语言(C,C++等)。因此,如果你想更改默认输出文件的名字,你需要使用-o选项改变SWIG生成的包装文件的后缀。例如:

  1. $ swig -c++ -python -o example_wrap.cpp example.i

SWIG的输出文件一般包含用于构建目标语言扩展的所有代码。SWIG不是存根编译器,通常也不需要需要修改输出文件(如果你看看输出文件,你可能就不想这样做了)。为构建最终的扩展模块,SWIG的输出文件需要与你的剩下的C/C++代码一起编译问共享库。

对多数目标语言来说,SWIG同时还会生成代理类(proxy class)的文件。这些语言独立的特别文件默认的输出文件夹和生成的C/C++包装文件是一样的。可以使用-outdir选项修改。例如:

  1. $ swig -c++ -python -outdir pyfiles -o cppfiles/example_wrap.cpp example.i

如果目录cppfiles和pyfiles存在,下面将产生:

  1. cppfiles/example_wrap.cpp
  2. pyfiles/example.py

如果指定了-outcurrentdir选项(不指定-o),SWIG的表现和典型的C/C++编译器一样,默认输出就是当前文件夹。没有这个选项,输出文件夹就是输入文件的路径。如果-o选项和-outcurrentdir选项一起使用,-outcurrentdir被忽略,如果不指定-outdir,语言特定的文件和生成的C/C++文件输出位置相同。

5.1.3 注释

C/C++样式的可以出现在接口文件的任何地方。在先前版本的SWIG中,注释被用于生成文档文件。但是,这个特征现在正被修复,将会在以后的发行中重新可用。

5.1.4 C预处理器

像C语言预处理器一样,SWIG通过增强的C语言预处理器处理所有的输入文件。所有的标准预处理特性它都支持,包括:文件包含、条件编译和宏。但是,#include语句是被忽略的,除非你指定了-includeall命令行参数。禁用包含的原因是因为SWIG有时候被用于处理原始的C语言头文件。在这种情况下,通常你只是想扩展那些包含在提供头文件中的函数到扩展模块中,而不是该头文件所包含的所有定义(比如,系统头文件、C语言库等)。

还应该注意的是,SWIG预处理器会跳过所有包含在%{...%}块中的代码。除此之外,预处理器还提供了一些增强宏,用起来比常规C语言预处理器更强大。这些扩展在预处理章节会描述的。

5.1.5 SWIG指令

绝大多数的SWIG操作是通过特殊的指令控制的,它们总是带有\$前置符号,主要目的是为了和C语言的声明区分开来。这些指令被用于给SWIG提供线索或改变SWIG解释器的行为。

因为SWIG指令不是合法的C语言语法,通常不可能在包含文件中书写。但是,SWIG指令可通过使用C语言的条件编译写在包含文件中,比如:

  1. /* header.h --- Some header file */
  2. /* SWIG directives -- only seen if SWIG is running */
  3. #ifdef SWIG
  4. %module foo
  5. #endif

SWIG是一个特殊的预处理器符号,它在输入文件被解释时是SWIG程序定义的。

5.1.6 解释器的局限

尽管SWIG可以解释多数的C/C++声明,但它没有完全实现C/C++解释器。大部分的限制是关于非常复杂的类型声明和某些高级C++特性。特别是一下这些当前不支持的特性:

  • 非传统的类型声明。例如,SWIG不支持下面这样的声明(尽管在C中时合法的):

    1. /* Non-conventional placement of storage specifier (extern) */
    2. const int extern Number;
    3. /* Extra declarator grouping */
    4. Matrix (foo); // A global variable
    5. /* Extra declarator grouping in parameters */
    6. void bar(Spam (Grok)(Doh));

    实践中,很少(如果真有的话)有C程序员真得像这样写代码,因为这样的代码在编程书籍里几乎没被提及过。但是,如果你感到困惑,这样做了,你会发现SWIG不能正常工作(尽管我也不知道你为什么还是要这么做)。

  • 不推荐直接在C++源代码文件上运行SWIG。一般方法是给SWIG提供带有C++定义和声明的头文件。主要原因是,如果SWIG遇到了带作用域的声明或定义时(在C++源文件中很常见),它会直接忽略,除非对符号的声明进行了早期解析。例如:

    1. /* bar not wrapped unless foo has been defined and
    2. the declaration of bar within foo has already been parsed */
    3. int foo::bar(int) {
    4. ... whatever ...
    5. }
  • 某些C++高级特性如嵌套类也支持的不全面。请查看C++嵌套类章节获取更多信息。

当遇到解释性错误时,可以使用条件编译跳过相关代码。例如:

  1. #ifndef SWIG
  2. ... some bad declarations ...
  3. #endif

或者,你可以直接从接口文件中将其删除。

SWIG不提供对C++解释器的完全支持的原因之一是,它被设计用来支持不完全的规格说明、能处理宽泛的C/C++数据类型(如,即使没有类定义或数据类型透明,SWIG也可以生成接口)。不幸的是,这种方式让它很难实现C/C++解释器的某些特性,因为编译器使用类型信息帮助解释非常复杂的声明==(for the truly curious, the primary complication in the implementation is that the SWIG parser does not utilize a separate typedef-name terminal symbol as described on p. 234 of K&R)==。