用Boost.Python + CMake + wxPython构建跨语言GUI程序<三>
- Class Exposition
类是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!