SWIG 安装本文使用了 SWIG 版本 2.0.4(参见 参考资料 获取下载站点的链接)。要构建和安装 SWIG,可按照典型的开源安装流程,在命令提示符下输入以下命令: 请注意,为前缀提供的路径必须是绝对路径。
SWIG 是一款不错的工具,可适合多种场景,其中包括:
此外,与 GNU Debugger 每次都需触发相比,SWIG 要容易调试得多。 Ruby 环境变量SWIG 生成包装器 使用 SWIG 编写 Hello World作为输入,SWIG 需要一个包含 ANSI %module test %constant char* Text = "Hello World with SWIG" 使用 SWIG 运行此代码: swig –ruby test.i 第二个代码段中的命令行在当前文件夹中生成一个名为 test_wrap.c 的文件。现在,您需要在此 bash$ gcc –fPIC –c test_wrap.c –I$RUBY_INCLUDE bash$ gcc –shared test_wrap.o –o test_wrap.so –lruby –L$RUBY_LIB 就这么简单。您已准备就绪,那就触发交互式 Ruby shell (IRB),输入 irb(main):001:0> require 'test_wrap' => true irb(main):002:0> Test.constants => ["Text"] irb(main):003:0> Test:: Text => "Hello World with SWIG" SWIG 可用于生成各种语言扩展,只需运行 也可以使用 SWIG 生成 SWIG 基础知识SWIG 接口文件语法是 模块名称的更多信息可通过指定 SWIG 接口文件必须以 模块初始化功能SWIG 拥有一个特殊指令 %module test %constant char* Text = “Hello World with SWIG” %init %{ printf(“Initialization etc. gets done here\n”); %} 现在重新启动 IRB。以下是在加载模块后得到的代码: irb(main):001:0> require 'test' Initialization etc. gets done here => true SWIG 常量
请注意,Ruby 常量必需以一个大写字母开头。所以,如果接口文件有诸如 bash$ swig –c++ –ruby test.i test.i(3) : Warning 801: Wrong constant name (corrected to 'Pi') 下面的示例包含大量常量。作为 %module test #define S_Hello "Hello World" %constant double PI = 3.1415 enum days {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; 清单 1 显示了 SWIG 的输出。 清单 1. 向 Ruby 公开 C 枚举:哪里出错了?test_wrap.c: In function `Init_test': test_wrap.c:2147: error: `Sunday' undeclared (first use in this function) test_wrap.c:2147: error: (Each undeclared identifier is reported only once test_wrap.c:2147: error: for each function it appears in.) test_wrap.c:2148: error: `Monday' undeclared (first use in this function) test_wrap.c:2149: error: `Tuesday' undeclared (first use in this function) test_wrap.c:2150: error: `Wednesday' undeclared (first use in this function) test_wrap.c:2151: error: `Thursday' undeclared (first use in this function) test_wrap.c:2152: error: `Friday' undeclared (first use in this function) test_wrap.c:2153: error: `Saturday' undeclared (first use in this function) 哎哟:发生什么事了?如果打开 test_wrap.c(清单 2),就可以看到问题。 清单 2. 使用 SWIG 生成的枚举代码rb_define_const(mTest, "Sunday", SWIG_From_int((int)(Sunday))); rb_define_const(mTest, "Monday", SWIG_From_int((int)(Monday))); rb_define_const(mTest, "Tuesday", SWIG_From_int((int)(Tuesday))); rb_define_const(mTest, "Wednesday", SWIG_From_int((int)(Wednesday))); rb_define_const(mTest, "Thursday", SWIG_From_int((int)(Thursday))); rb_define_const(mTest, "Friday", SWIG_From_int((int)(Friday))); rb_define_const(mTest, "Saturday", SWIG_From_int((int)(Saturday))); SWIG 从 Sunday、Monday 等变量中创建 Ruby 常量,但生成的文件中缺少 清单 3. 以正确的方式向 Ruby 公开 C 枚举%module test #define S_Hello "Hello World" %constant double PI = 3.1415 enum days {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; %{ enum days {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; %} 请注意,只有 %inline 特殊指令简介清单 3 有点奇怪 — 存在没有必要的 清单 4. 使用 %inline 指令减少代码副本%module test #define S_Hello "Hello World" %constant double PI = 3.1415 %inline %{ enum days {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}; %} %include 是一种均衡的清除方法在复杂的企业环境中,可能有一些 清单 5. 使用 %include 指令%module test %include "header.h" %{ #include "header.h" %}
常量足够多了:让我们公开一些函数开始学习 SWIG 的最简单方式是在接口文件中声明某个 %module test unsigned long factorial(unsigned long); 以下是我编译为 factorial.o 并在创建 test.so 时链接的 unsigned long factorial(unsigned long n) { return n == 1 ? 1 : n * factorial(n - 1); } 清单 6 显示了 Ruby 接口。 清单 6. 从 Ruby 测试代码irb(main):001:0> require 'test' => true irb(main):002:0> Test.factorial(11) => 39916800 irb(main):003:0> Test.factorial(34) => 0 Factorial 34 失败了,因为 unsigned 类型的 Ruby 到 C/C++ 的变量映射让我们从简单的全局变量开始。请注意, int global_int1; long global_long1; float global_float1; double global_double1; 清单 7 显示了相同的接口。 清单 7. 向 Ruby 公开 C 接口%module test %inline %{ extern int global_int1; extern long global_long1; extern float global_float1; extern double global_double1; %} 现在,加载相应的 Ruby 模块以验证添加的 setter 和 getter 方法: irb(main):003:0> Test.methods […"global_float1", "global_float1=", "global_int1", "global_int1=", "global_long1", "global_long1=", "global_double1", "global_double1=", …] 现在访问变量就非常简单了: irb(main):004:0> Test.global_long1 = 4327911 => 4327911 irb(main):005:0> puts Test.global_long1 => 4327911 特别有趣的是 Ruby 转换 清单 8. 在 Ruby 和 C/C++ 之间的类型映射irb(main):009:0> Test::global_long1.class => Fixnum irb(main):010:0> Test::global_int1.class => Fixnum irb(main):011:0> Test::global_double1.class => Float irb(main):012:0> Test::global_float1.class => Float 将结构和类从 C++ 映射到 Ruby向 Ruby 公开结构和类与 清单 9. 向 Ruby 公开结构和相关接口%module test %inline %{ typedef struct Point { int x; int y; }; extern float distance_between(Point& p1, Point& p2); %} 清单 10 展示了 Ruby 的用法。 清单 10. 从 Ruby 验证 C/C++ 功能irb(main):002:0> a = Test::Point.new => #<Test::Point:0x2d04260> irb(main):003:0> a.x = 10 => 10 irb(main):004:0> a.y = 20 => 20 irb(main):005:0> b = Test::Point.new => #<Test::Point:0x2cce668> irb(main):006:0> b.x = 20 => 20 irb(main):007:0> b.y = 10 => 10 irb(main):008:0> Test.distance_between(a, b) => 14.1421356201172 这个使用模型应该很清楚地说明了,为什么 SWIG 是在设置基本代码的单元或集成测试框架时的一个优秀、方便的工具。 %defaultctor 和其他属性如果查看一个点的 x 和 y 坐标的默认值,可以看到它们显示为 0。这不是巧合。SWIG 为您的结构生成了默认的构造函数。可以通过在接口文件中指定 清单 11. 没有针对 C++ 结构的默认构造函数%module test %nodefaultctor Point; %inline %{ typedef struct Point { int x; int y; }; %} 现在还需要为 irb(main):005:0> a = Test::Point.new TypeError: allocator undefined for Test::Point from (irb):5:in `new' from (irb):5 可通过在接口文件中指定 C++ 继承和 Ruby 接口为简单起见,假设接口函数中有两个 清单 12. SWIG 接口处理 C++ 继承irb(main):003:0> a = Test::Derived.new => #<Test::Derived:0x2d06270> irb(main):004:0> a.instance_of? Test::Derived => true irb(main):005:0> a.instance_of? Test::Base => false irb(main):006:0> Test::Derived < Test::Base => true irb(main):007:0> Test::Derived > Test::Base => false irb(main):008:0> a.is_a? Test::Derived => true irb(main):009:0> a.is_a? Test::Base => true 该处理过程没有使用 Warning 802: Warning for Derived d: base Base2 ignored. Multiple inheritance is not supported in Ruby. 坦诚地讲,SWIG 不能出错,因为 Ruby 不支持多个继承。SWIG 要正常工作,您需要在命令行中传递 bash$ swig -ruby -minherit -c++ test.i 一定要了解 SWIG 如何处理多重继承。 清单 13. 使用 Ruby 模拟多个继承class Base1 module Impl # Define Base1 methods here end include Impl end class Base2 module Impl # Define Base2 methods here end include Impl end class Derived module Impl include Base1::Impl include Base2::Impl # Define Derived methods here end include Impl end 让我们验证一下来自 Ruby 接口的声明。 清单 14. Ruby 类中包含的多个模块irb> Test::Derived.included_modules => [Test::Derived::Impl, Test::Base::Impl, Test::Base2::Impl, Kernel] irb> Test::Derived < Test::Base => nil irb> Test::Derived < Test::Base2 => nil 请注意,类层次结构测试失败了(理应如此),但对于应用程序开发人员来说, 指针和 Ruby 接口Ruby 没有与指针类似的东西,那么接受或返回指针的 void addition(const int* n1, const int* n2, int* result) { *result = *n1 + *n2; } 为解决这个问题,SWIG 引入了类型映射 的概念。您能够灵活地将您想要的 Ruby 类型映射到 %module Test %include typemaps.i void addition (int* INPUT, int* INPUT, int* OUTPUT); %{ extern void addition(int*, int*, int*); %} 现在,从 Ruby 试用代码 %typemap(in) int* { … type conversion code from Ruby to C/C++ } 同样地,从 %typemap(out) int* { … type convesion code from C/C++ to Ruby } 类型映射不只是为指针带来了方便:可将它们用于 Ruby 与 |
|