驭风万里无垠

用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!

posted on 2009-08-10 21:33 skyscribe 阅读(1077) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2009年12月>
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

导航

统计

常用链接

留言簿(3)

随笔分类

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜