你將學到什么
- 在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>>();
}