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_projectscitbx/array_family/boost_python/regress_test_ext.cppscitbx/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>>();
}
posted @ 2018-05-25 11:08  银魔术师  阅读(1619)  评论(0编辑  收藏  举报