SWIG入門3: C/C++初級特性

衆所周知,SWIG這個項目創建的緣由,是爲你們提供簡潔而又天然的腳本語言接口。什麼是簡潔而天然呢?它的意思就是C/C++的函數就直接被封裝爲python的函數,class就被封裝成python的class。 這樣你們用起來就不會變扭。下面講一講一些SWIG所支持的初級的C/C++特性。python

1 函數linux

函數乃是代碼複用之源。SWIG對於函數的封裝之道至簡。封裝完以後在python裏直接做爲模塊的方法調用。數組

%module example
int fact(int n);

>>> import example
>>> print example.fact(4)
24
>>>

2 全局變量函數

c語言中的各類類型的變量在python中均可以使用。全局變量被放在模塊的cvar這個變量中。this

//file: foo.c
#include <stdio.h>
int bar = 2;
float barfloat = 3.14;
double bardouble=3.1415926;
short barshort=10000;
long  barlong=200;
long long barlonglong=2000000000000ll;
unsigned barunsigned = 200;


int barFunc()
{
    printf("this is bar %d.\n"
            "barfloat %f\n"
            "bardouble %f\n"
            "barshort %hd\n"
            "barlong  %ld\n"
            "barlonglong %lld\n"
            "barunsigned %u\n"
            ,bar,barfloat,
            bardouble,barshort,
            barlong,barlonglong,
            barunsigned);
    return 0;
}

//file: foo.i
%module foo
%{
extern int bar;
extern float barfloat;
extern double bardouble;
extern short  barshort;
extern long   barlong;
extern long long barlonglong;
extern unsigned barunsigned;
%}

int bar;
float barfloat;
double bardouble;
short  barshort;
long   barlong;
long long barlonglong;
unsigned barunsigned;
int barFunc();

 須要注意的是,全局變量必需在.i文件中extern一下。不然編譯foo_wrap.c的時候會報錯。指針

使用的時候直接使用foo.var.xxx就能夠了。好比code

[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> foo.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'bar'
>>> print foo.cvar.bar
2

特別值得注意的是, 每個類型的數字在python中也會作範圍檢查,若是賦值超過了該類型的範圍,python會拋overflowerror.orm

>>> foo.cvar.barunsigned=-1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: in variable 'barunsigned' of type 'unsigned int'

另外,假如修改一個const全局變量,會引起一個segment fault。 因此處理const全局變量的最好方法是使用%immutable 和%mutable。 這兩個關鍵字分別表明只讀和可寫的變量。對象

%immutable;
int barconst;

[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> foo.cvar.barconst=2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Variable barconst is read-only.
>>>

這樣作只會引起異常,而不會引起更致命的段錯誤。 %immutable指令會一直有效,直到你顯示的使用%mutable指令爲止。接口

假如你以爲cvar這個名字不夠酷,你也能夠爲他換一個別的名字。只要在執行swig時候使用-globals varname 參數。

swig -python -globals variable foo.i

3 SWIG的const變量和枚舉變量

除了直接使用C語言模塊中定義的變量,在SWIG腳本中,也能夠爲python腳本定義的const變量和枚舉變量。能夠用到的技術有#define, enum,%constant。 其中enum枚舉變量須要也寫進你的xxx_wrap.c代碼中去。

%{
enum People{Man,Woman};
%}
#define PI 3.1415
#define VERSION "1.0"

enum People{Man,Woman};
%constant int barconstant=100;

使用這種變量就不須要經過cvar了。由於這就是Python腳本自身定義的變量,和你的C語言的代碼無關。

Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> foo.VERSION
'1.0'
>>> foo.PI
3.1415000000000002
>>> foo.Woman
1
>>> foo.Man
0
>>> foo.barconstant
100

4 指針

由於PYTHON裏面並無指針,因此SWIG只是將指針處理成了一種對象。

%module foo
FILE* fopen(const char* fname,const char* mode);
int fputs(const char*,FILE*);
int fclose(FILE*);

咱們能夠直接將庫函數封裝起來使用。

[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import foo
>>> foo.fopen("test","w")
<Swig Object of type 'FILE *' at 0xb741c620>
>>> f=foo.fopen("test","w")
>>> foo.fputs("1234\n",f)
1
>>> foo.fclose(f)
0

5 數組

PYTHON裏沒有數組。所以SWIG只能將數組的首地址做爲一個指針進行一次封裝。也就是說,在PYTHON中,你只能把這個數組當成指針來用。它能夠被傳遞給參數爲指針的函數做爲參數。也能夠被另外一個數組進行賦值,實際上賦值進行的就是內存拷貝,而並不會改變指針的地址。能夠看下面的例子。

//file: ary.c
#include <stdio.h>
int a[5]={1,2,3,4,5};
int b[6]={10,20,30,40,50,60};

void PrintArray(int *a,size_t n)
{
    size_t i=0;
    printf("{");
    for(i=0;i<n;i++)
    {
        printf("%d,",*a++);
    }
    printf("}\n");
}

void pa()
{
    PrintArray(a,sizeof(a)/sizeof(int));
}

void pb()
{
    PrintArray(b,sizeof(b)/sizeof(int));
}
//file: ary.i
%module ary
%{

extern int a[5];
extern int b[6];

extern void pa();
extern void pb();
%}

int a[5];
int b[6];
void pa();
void pb();

#file: testary.py
import ary
print "a is:"
ary.pa()
print str(ary.cvar.a)
print "b is:"
print str(ary.cvar.b)
ary.pb()

print "\n"
ary.cvar.a=ary.cvar.b

print "After a=b"
print "a is:"
ary.pa()
print str(ary.cvar.a)
print "b is:"
print str(ary.cvar.b)
ary.pb()

運行結果:

a is:
{1,2,3,4,5,}
_306720b7_p_int
b is:
_446720b7_p_int
{10,20,30,40,50,60,}


After a=b
a is:
{10,20,30,40,50,}
_306720b7_p_int
b is:
_446720b7_p_int
{10,20,30,40,50,60,}

能夠看到,運行a=b並無改變a指針指向的位置,而只是將b數組的前5個元素拷貝到a指針指向的位置而已。

 

你能夠在此處下載本文中的例子 https://dl.dropbox.com/u/35106490/swig3.tgz