Skip to content Skip to sidebar Skip to footer

How To Wrap Functions Overloaded By Type?

Suppose there is a class MyArray which implements an array of SomeType. It is written in C++ and wrapped into Python using boost::python. BOOST_PYTHON_MODULE(my_array_module) {

Solution 1:

In short, as long as the C++ functions have different parameter types, then each function can be exposed as the same Python function with separate calls to def(). Boost.Python will handle the dispatching based on type conversions. If the types are ambiguous, then one often needs to create and expose an auxiliary function that manually handles dispatching based on inspecting the boost::python::object arguments.


Here is a complete minimal example demonstrating accessing a mockup Counter class's data via either an index or a slice:

#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.structcounter
{
  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.intspam_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)
    ;
}

Interactive usage:

>>> 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

Note how when spam_getitem_index() throws a std::out_of_range exception, Boost.Python translates it the associated IndexError Python exception.

Post a Comment for "How To Wrap Functions Overloaded By Type?"