学习转自:JasonLiThirty
一、前言
1.1 boost
boost::python 用于将 C++ 的函数和对象导出, 方便 python 调用 对象和方法,用来实现 C++ 和 Python 的混合编程。
一言概之:boost 库的作用就是:将 C++ 的对象 转化 为 python 能直接调用的对象。用 C++ 写程序,用 python 解释器调用。
1.2 安装
库的安装参阅:C++ 和 Python 的混合编程 -Boost::python 的编译和配置 - 简书 (jianshu.com)
1.3 前菜
直接看一个 C++ 和 python 混合编程的例子:
#define BOOST_PYTHON_STATIC_LIB //boost::python 库的 config.hpp 中规定,如没定义 BOOST_PYTHON_STATIC_LIB ,则采用动态编译的库
#include <boost/python.hpp>
#include <iostream>
struct StructionData
{
void hello()
{
std::cout << "hello, this is boost::python sample!" << std::endl;
}
void printmsg()
{
std::cout << "print message done!" << std::endl;
}
};
BOOST_PYTHON_MODULE(Boost_Python_Sample)
{
//struct
boost::python::class_<StructionData>("StructionData")
.def("hello_p", &StructionData::hello)
.def("printmsg_p", &StructionData::printmsg);
}
上面第 6-16 行定义的一个 C++ 结构体,第 18-24 行写了一个 BOOST_PYTHON_MODULE,用来导出 C++ 的结构体,以供 python 使用。
BOOST_PYTHON_MODULE
里面传的为自定义的模块名。python 调用的 C++ 结构体的时候,需要导入该库名。boost::python::class_<StructionData>("StructionData")
,前面部分说明导出的是结构体,后面括号里StructionData
是自定义的对象名。- def 中第二个参数
StructionData::hello
为 C++ 结构体的函数名,第一个参数hello
为导出的函数名,供 python 调用的。
下面看 python 解释器运行上面的代码:
>>> import Boost_Python_Sample
>>> Ptr = Boost_Python_Sample.StructionData
>>> Ptr.hello_p()
hello, this is boost::python sample!
>>> Ptr.printmsg_p()
print message done!
二、C++ 数据类型的导出和调用
2.1 函数 Function
格式:boost::python::def(" 导出的 python 函数名 ", c++ 函数名)
看一个例子
void greet()
{
cout<< "Hello world!" <<endl;
}
BOOST_PYTHON_MODULE(Python_Wrapper)
{
boost::python::def("greet", greet);
}
//python 解释器运行
>>> import Python_Wrapper
>>> Python_Wrapper.greet()
'Hello world!'
2.2 类 class
2.2.1 无参构造
格式:
boost::python::class_<T>("TName")
.def("func1", &T::func1)
.def("func2", &T::func2);
- T 是需要被导出的 C++ 类名
- TName 是导出的自定义的类名
看一个例子:
class Person {
public:
void set(string name) {
m_name = name;
}
string get() {
return m_name;
}
private:
string m_name;
};
BOOST_PYTHON_MODULE(Python_Wrapper)
{
boost::python::class_<Person>("Person")
.def("set", &Person::set)
.def("get", &Person::get);
}
//python 解释器运行
>>> import Python_Wrapper
>>> msg = Python_Wrapper.Message()
>>> msg.Get()
''
>>> msg.Set('123')
>>> msg.Get()
'123'
2.2.2 有参构造
格式:boost::python::class_<T>("TName", boost::python::init<para>())
class Sum
{
public:
Sum(std::string data) :m_data(data) {}
Sum(double a, double b) :m_a(a), m_b(b) {}
void Set(std::string data)
{
m_data = data;
}
std::string Get()
{
return m_data;
}
double Result()
{
return m_a + m_b;
}
private:
std::string m_data;
double m_a;
double m_b;
};
BOOST_PYTHON_MODULE(Python_Wrapper)
{
boost::python::class_<Sum>("Sum", boost::python::init<std::string>())
.def(boost::python::init<double, double>())
.def("Set", &Sum::Set)
.def("Get", &Sum::Get)
.def("Result", &Sum::Result);
}
//python 解释器运行
>>> import Python_Wrapper
>>> s1 = Python_Wrapper.Sum("total")
>>> s1.Get()
'total'
>>> s2 = Python_Wrapper.Sum(1,2)
>>> s2.Result()
3.0
2.2.3 成员属性
格式:.def_readonly()/.def_readwrite()
class User
{
public:
User(std::string name) :m_name(name), m_number(-1) {}
std::string m_name;
int m_number;
};
BOOST_PYTHON_MODULE(Python_Wrapper)
{
boost::python::class_<User>("User", boost::python::init<std::string>())
.def_readonly("name", &User::m_name)
.def_readwrite("number", &User::m_number);
}
//python 解释器运行
>>> import Python_Wrapper
>>> user = Python_Wrapper.User("Jason")
>>> user.name = "Micky"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> user.number = 12345
>>> user.number
12345
2.2.4 增加类属性
格式:.add_property()
class MessagePro
{
public:
void Set(std::string msg)
{
m_msg = msg;
}
std::string Get()
{
return m_msg;
}
private:
std::string m_msg;
};
BOOST_PYTHON_MODULE(boost_python)
{
boost::python::class_<MessagePro>("MessagePro")
.add_property("info", &MessagePro::Get, &MessagePro::Set);
}
//python 解释器运行
>>> import Python_Wrapper
>>> msg = Python_Wrapper.MessagePro()
>>> msg.set() // 报错
>>> msg.get() // 报错
>>> msg.info
''
>>> msg.info = 'hello'
>>> msg.info
'hello'
2.2.5 继承
格式:boost::python::class_<T, boost::python::bases<TBase>>("TName")
必须告知导出原 C++ 类的继承关系,不然导出后类之间就没有了继承关系
告知类的继承关系关系后:
- 继承类自动继承了基类的 Python 方法(即包装了的 c++ 成员函数)
- 即使是基类指针指向继承类对象,多态的函数也能够找到相应继承类的对应函数
class Base
{
public:
virtual ~Base() {};
virtual std::string Name()
{
return "Base";
}
};
class Derived : public Base
{
public:
std::string Name()
{
return "Derived";
}
};
void BaseName(Base *base)
{
std::cout << base->Name().c_str() << std::endl;
}
void DerivedName(Derived *derived)
{
std::cout << derived->Name().c_str() << std::endl;
}
Base *factory()
{
return new Derived();
}
BOOST_PYTHON_MODULE(boost_python)
{
//inherited
boost::python::class_<Base>("Base", boost::python::init<>())
.def("Name", &Base::Name);
boost::python::class_<Derived, boost::python::bases<Base>>("Derived")
.def("Name", &Derived::Name);
boost::python::def("BaseName", BaseName);
boost::python::def("DerivedName", DerivedName);
// 因为 factory 是生成一个新的 Direved 对象
//manage_new_object 告知 Python 生成一个指针指向一个新生成的 Python 对象,
boost::python::def("factory", factory, boost::python::return_value_policy<boost::python::manage_new_object>());
}
//python
>>> import Python_Wrapper
>>> obj = Python_Wrapper.factory()
>>> obj.Name()
'Derived'
>>> Python_Wrapper.BaseName(obj)
Derived
>>> Python_Wrapper.DerivedName(obj)
Derived
>>>
>>> objBase = Python_Wrapper.Base()
>>> objBase.Name()
'Base'
>>> Python_Wrapper.BaseName(objBase)
Base
>>> Python_Wrapper.DerivedName(objBase)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Boost.Python.ArgumentError: Python argument types in
Python_Wrapper.DerivedName(Base)
did not match C++ signature:
2.2.6 其他
虚函数,操作符,重载等类的一些属性,参阅C++ 和 Python 的混合编程 -C++ 数据类型的导出和调用 - 简书 (jianshu.com)
2.3 枚举 Enum
格式:boost::python::enum_<T>("TName")
enum MessageType
{
MT_START = 1,
MT_PROCESS,
MT_DONE,
MT_EXCEPTION
};
BOOST_PYTHON_MODULE(Python_Wrapper)
{
//enum
boost::python::enum_<MessageType>("MessageType")
.value("MT_START", MT_START)
.value("MT_PROCESS", MT_PROCESS)
.value("MT_DONE", MT_DONE)
.value("MT_EXCEPTION", MT_EXCEPTION);
}
//python
>>> import Python_Wrapper
>>> Python_Wrapper.MessageType.MT_START
Python_Wrapper.MessageType.MT_START
>>> int(Python_Wrapper.MessageType.MT_START)
1
>>> int(Python_Wrapper.MessageType.MT_DONE)
3
2.2 常量 const
2.2.1 基本常量
格式:boost::python::scope().attr
//1. 在模块中加入常量属性
BOOST_PYTHON_MODULE(Python_Wrapper)
{
//const
boost::python::scope().attr("yes") = 1;
boost::python::scope().attr("no") = 0;
}
//python
>>> import Python_Wrapper
>>> Python_Wrapper.yes
1
>>> Python_Wrapper.no
0
boost::python::scope()用于得到 当前的作用域,定义新的 scope 对象会改变当前的作用域
使用参数来构造一个新的 scope 对象会将关联的全局 python 对象更改为参数所持有的对象 直到作用域对象的生存期结束, 关联的全局 python 对象才会恢复到作用域对象之前的对象。
2.2.2 作用域
class Message
{
public:
void Set(std::string msg)
{
m_msg = msg;
}
std::string Get()
{
return m_msg;
}
private:
std::string m_msg;
};
//1. 在模块中加入常量属性
BOOST_PYTHON_MODULE(Python_Wrapper)
{
//const
boost::python::scope().attr("yes") = 1;
boost::python::scope().attr("no") = 0;
boost::python::class_<Message>("Message")
.def("Set", &Message::Set)
.def("Get", &Message::Get);
}
//python
>>> import Python_Wrapper
>>> Python_Wrapper.yes
1
>>> Python_Wrapper.no
0
///////////////////////////////////////////////////////
//2. 改变导出顺序,也没有问题,在模块中加入常量属性
BOOST_PYTHON_MODULE(boost_python)
{
boost::python::class_<Message>("Message")
.def("Set", &Message::Set)
.def("Get", &Message::Get);
//const
boost::python::scope().attr("yes") = 1;
boost::python::scope().attr("no") = 0;
}
//python
>>> import Python_Wrapper
>>> Python_Wrapper.yes
1
>>> Python_Wrapper.no
0
//////////////////////////////////////////////////////
//3. 如果使用 boost::python::scope 对象,则改变了当前的作用域,yes 和 no 成了 message 类的属性
BOOST_PYTHON_MODULE(boost_python)
{
//Change the current scope
boost::python::scope newScope = boost::python::class_<Message>("Message")
.def("Set", &Message::Set)
.def("Get", &Message::Get);
//const Defined in the current scope(Message)
boost::python::scope().attr("yes") = 1;
boost::python::scope().attr("no") = 0;
}
//python
>>> import Python_Wrapper
>>> Python_Wrapper.yes
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'Python_Wrapper' has no attribute 'yes'
>>> Python_Wrapper.no
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: module 'Python_Wrapper' has no attribute 'no'
>>> msg = Python_Wrapper.Message()
>>> msg.yes
1
>>> msg.no
0
//4. 使用 boost::python::scope 定义了新的域对象,改变了当前的作用域,这个对象出了作用域,则会恢复为之前的域对象
BOOST_PYTHON_MODULE(Python_Wrapper)
{
//const
{
boost::python::scope newScope = boost::python::class_<Message>("Message")
.def("Set", &Message::Set)
.def("Get", &Message::Get);
boost::python::scope().attr("yes") = 1;
boost::python::scope().attr("no") = 0;
}
boost::python::scope().attr("exist") = 1;
boost::python::scope().attr("none") = 0;
}
//python
>>> import Python_Wrapper
>>> msg = Python_Wrapper.Message()
>>> msg.yes
1
>>> msg.no
0
>>> Python_Wrapper.exist
1
>>> Python_Wrapper.none
0
上面介绍了如何将 C++ 代码导出为 python 解释器能够调用的对象。因此我们可以利用 Boost::Python 从 Python 调用 C ++ 代码。
三、C++ 执行 python
这一部分内容介绍:C++ 调用执行 python 代码。
bp::str code = "def fact(n):\n\treturn 1 if n==1 else n*fact(n-1)")
bp::object result = bp::exec(code)
bp::exec 的作用就是执行 python 代码。
3.1 exec 函数
函数作用:用来执行一个字符串形式 python 语句,或者表达式,并返回得到的计算结果。
函数原型:
boost::pythoin::api::object exec(boost::python::str string,
boost::python::api::object global=boost::python::api::object(),
boost::python::api::object local=boost::python::api::object());
参数:
- string:需要执行的语句或表达式字符串,如 result=2**10 #2^10
- global:需要执行的字符串是放在哪个全局作用域中。
- local:需要执行的字符串是放在哪个局部作用域中。
- 返回的值是 Boost::Python::api 中的 Object 对象。
- 可以使用 extract 来获得 C++ 类型的值。
举个例子:
//C++
boost::python::object main_module = import("__main__");
boost::python::object main_namespace = main_module.attr("__dict__");
boost::python::object return_value = exec("result = 2**10", global = main_namespace,local = main_namespace);
3.2 eval 函数
函数作用:用来执行一个 python 求值的表达式的字符串, 并返回得到的计算结果。
原型:
namespace bpa = boost::python::api;
bpa::object bpa::eval(boost::python::str string,
bpa::object global=bpa::object(), bpa::object local = bpa::object())
参数:
- string:需要执行的表达式字符串,如 2**10
- global:需要执行的字符串是放在哪个全局作用域中。
- local:需要执行的字符串是放在哪个局部作用域中。
- 返回的值是 Boost::Python::api中的 Object 对象。
举个例子:
//C++
boost::python::object main_module = import("__main__");
boost::python::object main_namespace = main_module.attr("__dict__");
boost::python::object return_value = eval("2**10", main_namespace,main_namespace);
3.3 eval 与 exec 区别
eval 与 exec 的调用形式相同,作用也基本相同,区别是:
eval :evaluate Python expression from str
用来直接运行一个表达式求得一个值,而不是运行一个语句,eval 需要有返回的值。
exec:execute python expression/statement from str
用来运行一个表达式或者运行一个语句,可以有返回值,也可以没有。
3.4 exec_file 函数
函数作用:执行 python 源文件 Execute python source code from file ‘filename’.
原型:
namespace bpa=boost::python::api;
bpa::object exec_file(boost::python::str filename,
bpa::object global=bpa::object(), bpa::object local=pba::object())
参数:
- filename:需要执行的路径和文件名。
- global:需要执行的字符串是放在哪个全局作用域中。
- local:需要执行的字符串是放在哪个局部作用域中。
举个例子:
//C++
boost::python::object main_module = import("__main__");
boost::python::object main_namespace = main_module.attr("__dict__");
boost::python::object simple = exec_file("D:\\demoPython\\example.py", main_namespace,main_namespace);
欢迎各位看官及技术大佬前来交流指导呀,可以邮件至 jqiange@yeah.net