Boost Python学习笔记(四)
你将学到什么
- 在Python中调用C++代码时的传参问题
基础类型
Python的字符串是常量,所以C++函数参数中的
std::string &
必须为const
修改源文件(main.cpp)
#include <iostream>
#include <boost/python.hpp>
#include "boost_wrapper.h"
using namespace boost::python;
using namespace boost::python::detail;
int main()
{
Py_Initialize();
if (!Py_IsInitialized())
{
std::cout << "Initialize failed" << std::endl;
return -1;
}
try
{
object sys_module = import("sys");
str module_directory(".");
sys_module.attr("path").attr("insert")(1, module_directory);
object module = import("zoo");
module.attr("show")();
}
catch (const error_already_set&)
{
PyErr_Print();
}
Py_Finalize();
return 0;
}
Python脚本如下(build/zoo.py)
import boost
def show():
boost.add(2, 4)
boost.xstr("fwd")
if __name__ == '__main__':
pass
导出头文件如下(include/boost_wrapper.h)
#pragma once
#include <string>
void add(int x, int y);
void xstr(std::string const &x);
导出实现如下(src/boost_wrapper.cpp)
#include <iostream>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include "boost_wrapper.h"
using namespace boost::python;
using namespace boost::python::detail;
void add(int x, int y)
{
std::cout << "add: " << x + y << std::endl;
}
void xstr(std::string const &x)
{
std::cout << "string: " << x << std::endl;
}
BOOST_PYTHON_MODULE_INIT(boost)
{
def("add", add);
def("xstr", xstr);
}
标准库
修改源文件(main.cpp)
#include <iostream>
#include <boost/python.hpp>
#include "boost_wrapper.h"
using namespace boost::python;
using namespace boost::python::detail;
int main()
{
Py_Initialize();
if (!Py_IsInitialized())
{
std::cout << "Initialize failed" << std::endl;
return -1;
}
try
{
object sys_module = import("sys");
str module_directory(".");
sys_module.attr("path").attr("insert")(1, module_directory);
object module = import("zoo");
module.attr("show")();
}
catch (const error_already_set&)
{
PyErr_Print();
}
Py_Finalize();
return 0;
}
方式一
这种方式主要使用
vector_indexing_suite
定义一个新的Vector类型XVec
Python脚本如下(build/zoo.py)
import boost
def show():
l = boost.XVec()
l.append(2)
l.append(3)
l.append(4)
boost.show_list(l)
for i in l:
print(i)
if __name__ == '__main__':
pass
导出头文件如下(include/boost_wrapper.h)
#pragma once
#include <vector>
void show_list(std::vector<int> &v);
导出实现如下(src/boost_wrapper.cpp)
#include <iostream>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include "boost_wrapper.h"
using namespace boost::python;
using namespace boost::python::detail;
void show_list(std::vector<int> &v)
{
for (auto item : v)
{
std::cout << item << " ";
}
std::cout << std::endl;
v.push_back(7);
}
BOOST_PYTHON_MODULE_INIT(boost)
{
class_<std::vector<int> >("XVec").def(vector_indexing_suite<std::vector<int> >());
def("show_list", show_list);
}
方式二
这种方式主要是通过
boost :: python :: converter :: registry :: push_back
函数来注册自定义转换函数,主要实现两个函数convertible
(用于检测Python侧传入的对象是否符合转换条件,比如是不是迭代器、里面的元素类型是不是对的等,这边只是简单实现下)和construct
(提取Python侧传入的对象元素,然后构造C++侧的对象,这边也只是简单实现了下),高级实现方式可以参考cctbx_project的scitbx/array_family/boost_python/regress_test_ext.cpp
和scitbx/boost_python/container_conversions.h
文件,不过参数必须是右值(注意函数参数的const修饰符不能删)
Python脚本如下(build/zoo.py)
import boost
def show():
boost.show_list([2,3,4])
if __name__ == '__main__':
pass
导出头文件如下(include/boost_wrapper.h)
#pragma once
#include <vector>
void show_list(std::vector<int> const &v);
导出实现如下(src/boost_wrapper.cpp)
#include <iostream>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/extract.hpp>
#include <boost/python/to_python_converter.hpp>
#include "boost_wrapper.h"
using namespace boost::python;
using namespace boost::python::detail;
void show_list(std::vector<int> const &v)
{
for (auto item : v)
{
std::cout << item << " ";
}
std::cout << std::endl;
}
template<class ContainerType>
class from_python_list
{
public:
from_python_list()
{
boost::python::converter::registry::push_back(&convertible, &construct, boost::python::type_id<ContainerType>());
}
static void* convertible(PyObject *obj_ptr)
{
if (!(PyList_Check(obj_ptr)
|| PyTuple_Check(obj_ptr)
|| PyIter_Check(obj_ptr)
|| PyRange_Check(obj_ptr)
|| (PyObject_HasAttrString(obj_ptr, "__len__") && PyObject_HasAttrString(obj_ptr, "__getitem__"))))
return 0;
return obj_ptr;
}
static void construct(PyObject *obj_ptr, boost::python::converter::rvalue_from_python_stage1_data* data)
{
boost::python::handle<> obj_iter(PyObject_GetIter(obj_ptr));
void *storage = ((boost::python::converter::rvalue_from_python_storage<ContainerType>*)data)->storage.bytes;
new (storage) ContainerType();
data->convertible = storage;
ContainerType &result = *((ContainerType*)storage);
while (true)
{
boost::python::handle<> py_hdl(boost::python::allow_null(PyIter_Next(obj_iter.get())));
if (PyErr_Occurred())
boost::python::throw_error_already_set();
if (!py_hdl.get())
break;
boost::python::object py_obj(py_hdl);
boost::python::extract<typename ContainerType::value_type> obj(py_obj);
result.push_back(obj);
}
}
};
BOOST_PYTHON_MODULE_INIT(boost)
{
def("show_list", show_list);
from_python_list<std::vector<int>>();
}