接下來是函數部分,在根目錄下邊新添加一個子項目,連同其子目錄,并且在根目錄的CMakeLists.txt里邊加入對應聲明:
mkdir Function;
touch Function/CMakeLists.txt;
touch Function/test.cpp
對應的根目錄CMakeLists.txt后邊加入:
add_subdirectory(Function)
編輯Function的CMakeLists.txt:
project(Function)
set(lib_target function)
include_directories(${Boost_INCLUDE_DIRS})
add_library(${lib_target} SHARED test.cpp)
set_target_properties(${lib_target} PROPERTIES PREFIX "")
target_link_libraries(${lib_target} ${Boost_LIBRARIES})
接下來就是實際的練習代碼,添于test.cpp里。
1> 最常見的自由函數,最基本的C函數:
//dummy function
void dummyFunc()
{
cout << "Dummy function called!" << endl;
}
對應的Wrapper為:
BOOST_PYTHON_MODULE(function)
{
def(fun, dummyFunc)
..................
..................
}
這里需要留意的是,對應的MODULE里邊的那個名字必須和CMakeLists.txt里邊制定的庫名字完全一樣,否則python導入對應的模塊時候會報錯誤。
編譯之,只需要:
cd ../Build;
make
ls lib/function.so
cd lib
在Python里邊測試,可以用了:
$python
Python 2.6.2 (r262:71600, Aug 8 2009, 19:23:16)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import function
>>> function.dummyFunc()
Dummy function called!
>>>
2> 接下來是函數調用里邊,參數和返回值生存期的問題,因為有可能函數返回了一個內部對象,必須要外部來釋放,或者返回對象指向參數對象的子對象,或者參數之間存在相互依賴等,這些都必須顯示指明,否則就可能被可惡的指針問題說羈絆。
下邊是一個很極端的例子:
////////////////////////////////////////////////////////////////////////////////
//calling policy
struct InnerType
{
int i;
int j;
};
struct RefType
{
float i;
double j;
};
struct ComposedType
{
InnerType contained;
RefType* ref;
};
//really bad ?
InnerType& Func(ComposedType& x, RefType* z)
{
x.ref = z;
return x.contained;
}
想正確的把最后這個Func導入到Python里邊用,就必須指明其參數之間的依賴關系以及返回值應用里邊的曲曲折折,就是z指向的對象必須又x來管理,而返回值則是x的一個子對象,這里用Policy來指定:
class_<ComposedType>("ComposedType")
.def_readwrite("contained", &ComposedType::contained)
.def_readonly("ref", &ComposedType::ref);
class_<RefType>("RefType")
.def_readwrite("i", &RefType::i)
.def_readwrite("j", &RefType::j);
class_<InnerType>("InnerType")
.def_readwrite("i", &InnerType::i)
.def_readwrite("j", &InnerType::j);
def("Func", Func,
return_internal_reference<1,
with_custodian_and_ward<1, 2> >()
);
這種繁瑣的情況,還是盡量少用來做公有接口的好吧。
一個測試的例子:
Python 2.6.2 (r262:71600, Aug 8 2009, 19:23:16)
[GCC 4.4.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import function
>>> x=function.ComposedType()
>>> x.contained.i=1
>>> x.contained.j=2
>>> z=function.RefType()
>>> z.i=1
>>> z.j=3
>>> y=function.Func(x,z)
>>> y.i
1
>>> y.j
2
>>> y
<function.InnerType object at 0x7fead87ff910>
>>>
這里使用了類成員變量的導出來構造其他的輔助參數對象,例子也很直觀。后邊在Class的例子里邊還有更詳盡的闡釋。
3> 函數重載
重載是個很好的工具,可以用同樣的名字來描述不同的實現,下邊的是一個成員函數重載的例子(其實和Free funciton的唯一差別就是聲明導出的時候,
要在class_<T>對象的那個.后邊加def,而一般函數只要直接Def即可):
////////////////////////////////////////////////////////////////////////////////
//Overloadding
struct X
{
bool f(int a)
{
return true;
}
bool f(int a, double b)
{
return true;
}
bool f(int a, double b, char c)
{
return true;
}
int f(int a, int b, int c)
{
return a + b + c;
};
};
聲明的時候,則要費時一點:
//helpers
bool (X::*fx1)(int) = &X::f;
bool (X::*fx2)(int, double) = &X::f;
bool (X::*fx3)(int, double, char)= &X::f;
int (X::*fx4)(int, int, int) = &X::f;
class_<X>("X")
.def("f", fx1)
.def("f", fx2)
.def("f", fx3)
.def("f", fx4)
;
上邊用了幾個輔助函數來指向同一個函數,然后將他們都導出到同一個python對象的同一個成員函數下邊即可。
Python里邊調用的例子:
>>> import function
>>> obj=function.X()
>>> help(obj.f)
Help on method f:
f(...) method of function.X instance
f( (X)arg1, (int)arg2) -> bool :
C++ signature :
bool f(X {lvalue},int)
f( (X)arg1, (int)arg2, (float)arg3) -> bool :
C++ signature :
bool f(X {lvalue},int,double)
f( (X)arg1, (int)arg2, (float)arg3, (str)arg4) -> bool :
C++ signature :
bool f(X {lvalue},int,double,char)
f( (X)arg1, (int)arg2, (int)arg3, (int)arg4) -> int :
C++ signature :
int f(X {lvalue},int,int,int)
這里help給出的提示已經包含了幾個重載的參數,其中第一個參數就是C++的this指針或者python的self。
>>> obj.f(1,1.1)
True
>>> obj.f(1,1, 3)
5
>>> function.X.f(obj, 1, 1.2, "msg")
True
上邊是兩種不同的參數調用X的重載成員函數版本。
4> 函數參數的默認值
這個是c++允許函數重載的另一種方式,簡單的方法是用一個簡單的wrapper來寫一些helper,如下:
////////////////////////////////////////////////////////////////////////////////
//Default args
int fn(int, double = 3.14, char const* = "hello")
{
return 1;
}
//wrapper
int fn1(int x) {return fn(x);}
int fn2(int x, double y) { return fn(x,y);}
導出的方式和上邊類似:
//default args
def("f", fn);
def("f", fn1);
def("f", fn2);
當然boost.python提供了一個宏來完成上邊的封裝,因此用下邊的方式則更簡單:
BOOST_PYTHON_FUNCTION_OVERLOADS(fn_overloads, fn, 1, 3);
def("fn", fn, fn_overloads());
上邊的宏第一個參數指定了重載函數的名字,第二個是原來的函數名,最后兩個參數這表示最少和最多能夠接受多少個可變參數。
調用示例:
>>> func = function.f
>>> help(func)
Help on built-in function f:
f(...)
f( (int)arg1, (float)arg2, (str)arg3) -> int :
C++ signature :
int f(int,double,char const*)
f( (int)arg1) -> int :
C++ signature :
int f(int)
f( (int)arg1, (float)arg2) -> int :
C++ signature :
int f(int,double)
>>> fn = function.fn
>>> help(fn)
Help on built-in function fn:
fn(...)
fn( (int)arg1 [, (float)arg2 [, (str)arg3]]) -> int :
C++ signature :
int fn(int [,double [,char const*]])
>>> func(1,2.2, "str")
1
>>> fn(1)
1
>>> fn(1, 2.2)
1
5> 類的成員函數參數的默認值
有些時候我們需要和類的成員函數打交道,所以對應的也有一個宏來完成那些繁雜的wrapper:
////////////////////////////////////////////////////////////////////////////////
//Mem_fun
struct Y
{
Y(int i, int j, int k=0, int p=1.2){}
void mem_fn(int i, int j = 0, double k=1.2, const std::string& str="msg"){}
};
BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(mem_fn_overloads, mem_fn, 1, 4);
宏的作用和上邊的那個用于普通函數的差不多。
這里邊有一個構造函數默認值的問題,參考下邊的optional模板:
//Mem_fn and optional init
class_<Y>("Y", init<int, int, optional<int, double> >())
.def("mem_fn", &Y::mem_fn, mem_fn_overloads());