103

Boost.Python C++导出基本用法

 6 years ago
source link: http://blog.soliloquize.org/2017/12/22/BoostPython-C%E5%AF%BC%E5%87%BA%E5%9F%BA%E6%9C%AC%E7%94%A8%E6%B3%95/?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Boost.Python C++导出基本用法

發表於 2017-12-22 07:30:34  

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

导出一个空的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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK