SWIG這個項目建立的原因,是為大家提供簡潔而又自然的腳本語言接口。什麼是簡潔而自然呢?它的意思就是C/C++的函數就直接被封裝為python的函數,class就被封裝成python的class。 這樣大家用起來就不會變扭。下面講一講一些SWIG所支持的初級的C/C++特性。
1 函數
函數乃是代碼復用之源。SWIG對於函數的封裝之道至簡。封裝完之後在python裡直接作為模塊的方法調用。
1 %module example
2 int fact(int n);
1 >>> import example
2 >>> print example.fact(4)
3 24
4 >>>
2 全局變量
c語言中的各種類型的變量在python中都可以使用。全局變量被放在模塊的cvar這個變量中。
01 //file: foo.c
02 #include <stdio.h>
03 int bar = 2;
04 float barfloat = 3.14;
05 double bardouble=3.1415926;
06 short barshort=10000;
07 long barlong=200;
08 long long barlonglong=2000000000000ll;
09 unsigned barunsigned = 200;
10
11
12 int barFunc()
13 {
14 printf("this is bar %d.\n"
15 "barfloat %f\n"
16 "bardouble %f\n"
17 "barshort %hd\n"
18 "barlong %ld\n"
19 "barlonglong %lld\n"
20 "barunsigned %u\n"
21 ,bar,barfloat,
22 bardouble,barshort,
23 barlong,barlonglong,
24 barunsigned);
25 return 0;
26 }
01 //file: foo.i
02 %module foo
03 %{
04 extern int bar;
05 extern float barfloat;
06 extern double bardouble;
07 extern short barshort;
08 extern long barlong;
09 extern long long barlonglong;
10 extern unsigned barunsigned;
11 %}
12
13 int bar;
14 float barfloat;
15 double bardouble;
16 short barshort;
17 long barlong;
18 long long barlonglong;
19 unsigned barunsigned;
20 int barFunc();
需要注意的是,全局變量必需在.i文件中extern一下。否則編譯foo_wrap.c的時候會報錯。
使用的時候直接使用foo.var.xxx就可以了。比如
1 [GCC 4.4.5] on linux2
2 Type "help", "copyright", "credits" or "license" for more information.
3 >>> import foo
4 >>> foo.bar
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 AttributeError: 'module' object has no attribute 'bar'
8 >>> print foo.cvar.bar
9 2
特別值得注意的是, 每一個類型的數字在python中也會做范圍檢查,如果賦值超過了該類型的范圍,python會拋overflowerror.
1 >>> foo.cvar.barunsigned=-1
2 Traceback (most recent call last):
3 File "<stdin>", line 1, in <module>
4 OverflowError: in variable 'barunsigned' of type 'unsigned int'
另外,假如修改一個const全局變量,會引發一個segment fault。 所以處理const全局變量的最好方法是使用%immutable 和%mutable。 這兩個關鍵字分別代表只讀和可寫的變量。
1 %immutable;
2 int barconst;
1 [GCC 4.4.5] on linux2
2 Type "help", "copyright", "credits" or "license" for more information.
3 >>> import foo
4 >>> foo.cvar.barconst=2
5 Traceback (most recent call last):
6 File "<stdin>", line 1, in <module>
7 AttributeError: Variable barconst is read-only.
8 >>>
這樣做只會引發異常,而不會引發更致命的段錯誤。 %immutable指令會一直有效,直到你顯示的使用%mutable指令為止。
假如你覺得cvar這個名字不夠酷,你也可以為他換一個別的名字。只要在執行swig時候使用-globals varname 參數。
view sourceprint?1 swig -python -globals variable foo.i
3 SWIG的const變量和枚舉變量
除了直接使用C語言模塊中定義的變量,在SWIG腳本中,也可以為python腳本定義的const變量和枚舉變量。可以用到的技術有#define, enum,%constant。 其中enum枚舉變量需要也寫進你的xxx_wrap.c代碼中去。
1 %{
2 enum People{Man,Woman};
3 %}
4 #define PI 3.1415
5 #define VERSION "1.0"
6
7 enum People{Man,Woman};
8 %constant int barconstant=100;
使用這種變量就不需要通過cvar了。因為這就是Python腳本自身定義的變量,和你的C語言的代碼無關。
01 Type "help", "copyright", "credits" or "license" for more information.
02 >>> import foo
03 >>> foo.VERSION
04 '1.0'
05 >>> foo.PI
06 3.1415000000000002
07 >>> foo.Woman
08 1
09 >>> foo.Man
10 0
11 >>> foo.barconstant
12 100
4 指針
因為PYTHON裡面並沒有指針,所以SWIG只是將指針處理成了一種對象。
1 %module foo
2 FILE* fopen(const char* fname,const char* mode);
3 int fputs(const char*,FILE*);
4 int fclose(FILE*);
我們可以直接將庫函數封裝起來使用。
01 [GCC 4.4.5] on linux2
02 Type "help", "copyright", "credits" or "license" for more information.
03 >>> import foo
04 >>> foo.fopen("test","w")
05 <Swig Object of type 'FILE *' at 0xb741c620>
06 >>> f=foo.fopen("test","w")
07 >>> foo.fputs("1234\n",f)
08 1
09 >>> foo.fclose(f)
10 0
5 數組
PYTHON裡沒有數組。因此SWIG只能將數組的首地址作為一個指針進行一次封裝。也就是說,在PYTHON中,你只能把這個數組當成指針來用。它可以被傳遞給參數為指針的函數作為參數。也可以被另一個數組進行賦值,實際上賦值進行的就是內存拷貝,而並不會改變指針的地址。可以看下面的例子。
01 //file: ary.c
02 #include <stdio.h>
03 int a[5]={1,2,3,4,5};
04 int b[6]={10,20,30,40,50,60};
05
06 void PrintArray(int *a,size_t n)
07 {
08 size_t i=0;
09 printf("{");
10 for(i=0;i<n;i++)
11 {
12 printf("%d,",*a++);
13 }
14 printf("}\n");
15 }
16
17 void pa()
18 {
19 PrintArray(a,sizeof(a)/sizeof(int));
20 }
21
22 void pb()
23 {
24 PrintArray(b,sizeof(b)/sizeof(int));
25 }
01 //file: ary.i
02 %module ary
03 %{
04
05 extern int a[5];
06 extern int b[6];
07
08 extern void pa();
09 extern void pb();
10 %}
11
12 int a[5];
13 int b[6];
14 void pa();
15 void pb();
01 #file: testary.py
02 import ary
03 print "a is:"
04 ary.pa()
05 print str(ary.cvar.a)
06 print "b is:"
07 print str(ary.cvar.b)
08 ary.pb()
09
10 print "\n"
11 ary.cvar.a=ary.cvar.b
12
13 print "After a=b"
14 print "a is:"
15 ary.pa()
16 print str(ary.cvar.a)
17 print "b is:"
18 print str(ary.cvar.b)
19 ary.pb()
運行結果:
view sourceprint?01 a is:
02 {1,2,3,4,5,}
03 _306720b7_p_int
04 b is:
05 _446720b7_p_int
06 {10,20,30,40,50,60,}
07
08
09 After a=b
10 a is: www.2cto.com
11 {10,20,30,40,50,}
12 _306720b7_p_int
13 b is:
14 _446720b7_p_int
15 {10,20,30,40,50,60,}
可以看到,運行a=b並沒有改變a指針指向的位置,而只是將b數組的前5個元素拷貝到a指針指向的位置而已。