类是OO编程关于封装的基本单元,围绕其相关的主要是虚函数、成员变量、成员函数、构造、析构、继承、多态的问题。
本示例新建于一个ExposeClass的子项目目录,并且生成expose_class.so这个模块库。
前边已经穿插了一些基本的使用方法,下边是一些更复杂的东西。
1> 构造函数重载和数据成员、属性
c++里边运行声明多个构造函数,并且一旦用户声明一个,编译器不再生成默认构造函数;对于数据成员有简单的public/private/protected三种访问权限控制,以及很多人采用的getter/setter来模拟的属性接口。对于构造函数重载,只需要多声明几个init函数即可,对于属性和数据成员,可如下施为:
///////////////////////////////////////////////////////////////////////////////
struct World
{
//Constructors
World(int i){ this->msg = "int parameter";}
World(std::string msg) {this->msg = msg;}
//Simple interface
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
std::string msg;
//Data variable
int name;
int value;
//Property
std::string getProp() {return prop;}
void setProp(const std::string& val) {prop = val;}
private:
std::string prop;
};
上述的类可如下导出:
BOOST_PYTHON_MODULE(expose_class)
{
class_<World>("World", init<std::string>())
.def(init<int>())
.def("greet", &World::greet)
.def("set", &World::set)
.def_readonly("name", &World::name)
.def_readwrite("value", &World::value)
.add_property("roprop", &World::getProp)
.add_property("prop", &World::getProp, &World::setProp)
;
上边的init模板函数用于制定额外的构造函数。
read_only指定对应的name成员为只读,read_write则说明对应的value是可读写的数据成员。
add_property则添加一个对应名字的Python熟悉,第一个参数为get方法的接口,第二个为set方法接口,如果只有一个被提供,那么对应的就是一个只读属性。
调用例子如下:
>>> import expose_class as cls
>>> cls.World("str")
<expose_class.World object at 0x7f3e29529e10>
>>> cls.World(124)
<expose_class.World object at 0x7f3e29529d60>
>>> obj=_
>>> obj.name="name"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.name
1
>>> obj.value="value"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
None.None(World, str)
did not match C++ signature:
None(World {lvalue}, int)
>>> obj.value=12
>>> obj.roprop="name"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> obj.roprop
''
>>> obj.prop = "name"
>>> obj.name
1
>>> obj.value
12
>>>
上边例子中,企图对read_only的变量或者属性进行赋值的时候均发生异常而报错。
2> 抽象类
所谓的抽象类意指其构造函数不能被外部调用,需要通过factory method来返回一个多态的指针,指向构造的子对象。
下边是个简单的抽象类的例子:
///////////////////////////////////////////////////////////////////////////////
//Abstract class
struct Abstract
{
static std::string msg;
void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
static Abstract* CreateInstance()
{
return new Abstract();
}
};
std::string Abstract::msg = "default value";
导出的情况如下:
class_<Abstract>("Abstract", no_init)
.def("greet", &Abstract::greet)
.def("set", &Abstract::set)
;
def("CreateAbstract", Abstract::CreateInstance,
return_value_policy<manage_new_object>());
这里的no_init就是指定没有构造函数可调用。
>>> import expose_class as test
>>> test.Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: This class cannot be instantiated from Python
>>> obj=test.CreateAbstract()
>>> obj.greet()
'default value'
调用的时候,如果调用了这个Abstract的构造,就会抛出异常。对于factory method,由于返回的对象是函数内部构造于堆上的,因此用return_value_policy显示的指明其生存期。
3> 继承和虚函数
虚函数是c++实现多态机制和OO动态的核心所在,这里主要关注两种情况,一种是纯虚函数(强制之类提供自己的实现),一种是基类提供了默认实现的普通虚函数。
对于继承而言,必须通过一个wrapper类来告知boost.python各个类之间的继承关系。
纯虚函数和一般虚函数的区别仅仅是没有默认实现,在导出的时候,一般虚函数要多提供一个基类默认实现的函数,而纯虚函数需要特别修饰声明一下。
参考下边这两个类:
///////////////////////////////////////////////////////////////////////////////
//Inheritance
struct Base
{
virtual ~Base() {};
//Virtual interface
virtual int fun() = 0;
virtual int func1(int i)
{
cout << "Default implementtion of func1" << endl;
return 0;
}
};
struct Derived : Base
{
virtual int fun()
{
cout << "Derived implementation of fun!" << endl;
return 0;
}
virtual int func1(int i)
{
cout << "Derived implementation of func1!" << endl;
return 0;
}
};
接下来需要声明一个wrapper类作为基础来导出,告知基类信息。这里用了两个函数,一个是纯虚函数fun, 一个是普通虚函数func1.
对于纯虚函数,只需要通过get_override得到具体的函数对象并调用即可(因为基类没有实现);
对于纯虚函数,则要根据get_override的结果决定是否是基类,然后分别调用对应的函数。
具体的wrapper代码如下:
struct BaseWrap : Base, wrapper<Base>
{
//pure virtual implementation
int fun()
{
return this->get_override("fun")();
}
//pure virtual with default implementation
int func1(int i)
{
if (override f = this->get_override("func1"))
{
return f(i);
}
else
return Base::func1(i);
}
int default_func1(int i) {return Base::func1(i);}
};
导出的代码:
class_<BaseWrap, boost::noncopyable>("Base")
.def("fun", pure_virtual(&Base::fun))
.def("fun1", &Base::func1, &BaseWrap::default_func1)
;
class_<Derived, bases<Base> >("Derived");
这里在导出Derived的时候,直接用bases模板参数告知Boost.python它是Base的之类(不是BaseWrap的)。
4> Factory method
对于缺乏virtual contructor机制的c++来说,Factory method是一个很常用的生成之类对象的构造方法,如下(为简单起见,只生成一个之类对象):
//Free functions on polymophism
void baseFunc(Base* ptr)
{
ptr->fun();
ptr->func1(1);
}
void derivedFunc(Derived* ptr)
{
ptr->func1(2);
}
Base* factoryMethod() { return new Derived; }
这里也导出这个接口:
class_<Derived, bases<Base> >("Derived");
def("baseFunc", baseFunc);
def("derivedFunc", derivedFunc);
def("factoryMethod", factoryMethod,
return_value_policy<manage_new_object>());
调用的例子:
>>> base_obj=test.Base()
>>> dev_obj=test.Derived()
>>> base_obj.fun()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Pure virtual function called
>>> dev_obj.fun()
Derived implementation of fun!
0
>>> base_obj.fun1(1)
Default implementtion of func1
0
>>> dev_obj.fun1(1)
Derived implementation of func1!
0
>>> poly_obj=test.factoryMethod()
>>> test.baseFunc(poly_obj)
Derived implementation of fun!
Derived implementation of func1!