用一些简单的例子来记录下常见用法。

导出模块

导出一个空的Python模块,

BOOST_PYTHON_MODULE(py_sample) {
}

在相应的Python代码中可以dir查看导出模块的内容,

import py_sample
print dir(py_sample)  # ['__doc__', '__name__', '__package__']

导出函数

在C++中定义函数,并做相应导出,

std::string Foobar() {
        return "foobar";
}

BOOST_PYTHON_MODULE(py_sample) {
    def("foobar", Foobar);
}

对于函数参数以及返回值是简单类型,比如int/long/float/double/bool/string等,Boost会自动进行转化。

导出类

直接来看具体的例子,

class Base {
    public:
        Base(std::string name):
            name_(name),
            value1_(0),
            value2_(0) {
        }

        Base(int value1, int value2): name_("") {
            value1_ = value1;
            value2_ = value2;
        }

        std::string GetName() {
            return name_;
        }

        int getValue2() {
            return value2_;
        }

    public:
        std::string name_;
        int value1_;

    private:
        int value2_;
};

BOOST_PYTHON_MODULE(py_sample) {
    class_<Base>("Base", init<std::string>())
            .def(init<int, int>())
            .def("get_name", &Base::GetName)
            .def_readonly("value1", &Base::value1_)
            .add_property("value2", &Base::getValue2)
            ;
}

构造函数导出

对于构造函数,如果只存在一个构造函数,则直接在类名后跟上init块,如果存在多个构造函数,则其余构造函数以.def(init<...>())进行导出。

成员函数导出

对于函数来说,通过def操作进行导出。

成员变量导出

对于类属性来说,如果是public的字段,可以通过def_readonlydef_readwrite进行导出,对于private字段,则需要通过add_property来导出,提供对应的getter/setter接口。

继承关系处理

考虑C++中的继承关系,实现一个Child类继承Base并直接导出,同时再定义导出一个处理基类指针Base*的函数,

class Child: public Base {
    public:
        Child(std::string name):
            Base(name) {
        }
};

void Process(Base* base) {
}

BOOST_PYTHON_MODULE(py_sample) {
    ...

    class_<Child>("Child", init<std::string>())
            ;

    def("process", &Process);
}

上面的导出方式在Python中会丢失继承关系,并且无法将Child实例传递给Process函数,

from py_sample import Base, Child, process
instance = Child("child")
print isinstance(instance, Child), isinstance(instance, Base)   # True, False

调用,

process(instance);

则会出现如下异常,

Boost.Python.ArgumentError: Python argument types in
    py_sample.process(Child)
did not match C++ signature:
    process(class Base *)

为了处理导出类的继承关系,需要在导出类的时候显示注明其基类,

class_<Child, bases<Base>>("Child", init<std::string>())
        ;

如此上述的isinstance判断会返回预期结果,process也能正常调用。

在Python中继承导出类

如果在C++类中定义了纯虚函数或虚函数,希望在Python中继承类并重载对应的函数,则需要通过wrapper进行一定处理,

class Base {
    public:
        Base(): value_(0) {
        }

        virtual int GetValue() = 0;

        virtual int Multiple(int v) {
            return value_ * v;
        }

        int TryGetValue() {
            return GetValue();
        }

        int TryMultiple(int v) {
            return Multiple(v);
        }

    private:
        int value_;
};

class BaseWrap: public Base, public wrapper<Base> {
    public:
        int GetValue() {
            return this->get_override("get_value")();
        }

        int Multiple(int v) {
            if (override f = this->get_override("multiple")) {
                return f(v);
            }
            return Base::Multiple(v);
        }

        int DefaultMultiple(int v) {
            return this->Base::Multiple(v);
        }
};

BOOST_PYTHON_MODULE(py_sample) {
    class_<BaseWrap, boost::noncopyable>("Base")
        .def("try_multiple", &Base::TryMultiple)
        .def("try_get_value", &Base::TryGetValue)
        .def("get_value", pure_virtual(&Base::GetValue))
        .def("multiple", &Base::Multiple, &BaseWrap::DefaultMultiple)
        ;
}

在Python中进行如下调用,

from py_sample import Base

class Child(Base):
    def multiple(self, v):
        return 1000

    def get_value(self):
        return 1000

child = Child()
base = Base()
print child.try_multiple(1)  # 1000
print base.try_multiple()  # 0

print child.try_get_value()  # 1000
print base.try_get_value()   # exception

上面定义的try_multipletry_get_value两个接口是为了说明以这种方式定义,可以在C++代码中获得Python中定义的重载效果。

函数默认参数处理

普通函数的默认参数处理,

int Foo(int a, int b=1) {
        return a * b;
}

BOOST_PYTHON_FUNCTION_OVERLOADS(FooOverloads, Foo, 1, 2)

BOOST_PYTHON_MODULE(py_sample) {
    def("foo", Foo, FooOverloads());
}

通过BOOST_PYTHON_FUNCTION_OVERLOADS进行包装,上面的1,2数字分别代表最少参数个数与最大参数个数。定义在类上的成员函数则通过BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS进行修饰,

class Base {
    public:
        int Foo(int a, int b=1) {
            return a * b;
        }
};

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(FooOverloads, Base::Foo, 1, 2)

BOOST_PYTHON_MODULE(py_sample) {
    class_<Base>("Base")
            .def("foo", &Base::Foo, FooOverloads())
            ;
}

函数overload处理

C++中可以定义多个overload函数,在导出的时候,对于有公共前缀参数的overload函数来说,可以通过BOOST_PYTHON_FUNCTION_OVERLOADS, BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS来帮助进行导出,

class Base {
    public:
        int Foo(int a, int b) {
            return a * b;
        }

        int Foo(int a, int b, int c) {
            return a * b * c;
        }
};

BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(FooOverloads, Base::Foo, 2, 3)

BOOST_PYTHON_MODULE(py_sample) {
    class_<Base>("Base")
            .def("foo", (int(Base::*)(int, int, int)) 0, FooOverloads())
            ;
}

导出枚举

枚举的导出简单很多,

enum Enum {
        FIRST = 0,
        SECOND
};

BOOST_PYTHON_MODULE(py_sample) {
    enum_<Enum>("Enum")
        .value("FIRST", FIRST)
        .value("SECOND", SECOND)
        ;
}

在Python中使用,

from py_sample import Enum
print Enum.FIRST, Enum.SECOND

参考