如何通过包装型重载函数?(How to wrap functions overloaded by t

2019-11-03 08:14发布

假设有一个类MyArray它实现的阵列SomeType 。 它是用C ++编写,并使用boost ::蟒蛇缠成Python。

BOOST_PYTHON_MODULE(my_array_module)
{
    class_<MyArray>("MyArray")
    // a few '.def's ...
    .def("__getitem__", ???)
    ;
}

__getitem__在Python函数或者可以采取一个索引并返回SomeType值,或采取一个切片对象,并返回切片。

还有就是如何处理在C重载函数++它们包装成不同的Python功能。 还有就是如何使Python中的重载函数,如果重载意味着不同数量的参数的个数。

但是,如何包装重载函数,如果他们通过arument类型不同? 我需要2个getitem在C ++函数。

const SomeType& getitem(PyObject *self, size_t pos) {
    // ...
}

MyArray getitem(PyObject *self, slice sl) {
    // ...
}

如果您尝试使用其包装BOOST_PYTHON_FUNCTION_OVERLOADS描述的方式在这里 ,它不会编译。

我可以做一个函数

PyObject* getitem(PyObject *self, PyObject *pos_or_slice) {
    extract<size_t> get_pos(pos_or_slice);
    if (get_pos.check()) {
        // return a single item (SomeType)
    }
    else {
        // return a slice (MyArray)
    }
}

但我不知道如何正确地包装MyArrayPyObject* ,这样,这将是与所产生的包装一致class_

Answer 1:

总之,只要在C ++函数具有不同的参数类型,则每个功能可以公开为相同的Python函数与单独的呼叫到def() Boost.Python的将根据类型转换处理的调度。 如果类型是不明确的,那么通常都需要对创建和公开的辅助功能调度;根据检查的是手动处理boost::python::object参数。


下面是一个完整的最小示例演示访问实体模型Counter经由任一个索引或切片类的数据:

#include <vector>
#include <boost/range/algorithm.hpp>
#include <boost/range/irange.hpp>
#include <boost/python.hpp>
#include <boost/python/slice.hpp>

/// @brief Mockup class that creates a range from 0 to N.
struct counter
{
  counter(std::size_t n)
  {
    data.reserve(n);
    boost::copy(boost::irange(std::size_t(0), n), std::back_inserter(data));
  }

  std::vector<int> data;
};

/// @brief Handle index access for counter object.
int spam_getitem_index(const counter& self, int index)
{
  // Attempt to convert to positive index.
  if (index < 0)
  {
    index += self.data.size();
  }

  // Check for valid range.
  if (index < 0 || self.data.size() <= index)
  {
      throw std::out_of_range("counter index out of range");
  }

  return self.data[index];
}

/// @brief Handle slicing for counter object.
boost::python::list spam_getitem_slice(
  const counter& self,
  boost::python::slice slice)
{
  namespace python = boost::python;
  python::list result;

  // Boost.Python will throw std::invalid_argument if the range would be
  // empty.
  python::slice::range<std::vector<int>::const_iterator> range;
  try
  {
    range = slice.get_indices(self.data.begin(), self.data.end());
  }
  catch (std::invalid_argument)
  {
    return result;
  }

  // Iterate over fully-closed range.
  for (; range.start != range.stop; std::advance(range.start, range.step))
  {
    result.append(*range.start);
  }
  result.append(*range.start); // Handle last item.
  return result;
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<counter>("Counter", python::init<int>())
    .def("__getitem__", &spam_getitem_slice)
    .def("__getitem__", &spam_getitem_index)
    ;
}

互动用法:

>>> from example import Counter
>>> counter = Counter(5)
>>> assert(counter[:]    == [0,1,2,3,4])
>>> assert(counter[:-2]  == [0,1,2])
>>> assert(counter[-2:]  == [3,4])
>>> assert(counter[::2]  == [0,2,4])
>>> assert(counter[1::2] == [1,3])
>>> assert(counter[100:] == [])
>>> assert(counter[1]    == 1)
>>> assert(counter[-1]   == 4)
>>> counter[100]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: counter index out of range
>>> counter[-100]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: counter index out of range

注意如何当spam_getitem_index()抛出一个std::out_of_range例外,Boost.Python的把它翻译相关IndexError Python异常。



文章来源: How to wrap functions overloaded by type?