接下来是函数部分,在根目录下边新添加一个子项目,连同其子目录,并且在根目录的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());