1

I want to create custom conversions from std::vector to Python list using boost python. For that I follow the to_python_converter approach. I used a typical converter structure, i.e.

template <class T, bool NoProxy = true>
struct vector_to_list {
  static PyObject* convert(const std::vector<T>& vec) {
    typedef typename std::vector<T>::const_iterator const_iter;
    bp::list* l = new boost::python::list();
    for (const_iter it = vec.begin(); it != vec.end(); ++it) {
      if (NoProxy) {
        l->append(boost::ref(*it));
      } else {
        l->append(*it);
      }
    }
    return l->ptr();
  }
  static PyTypeObject const* get_pytype() { return &PyList_Type; }
};

which I can use successfully in plenty of cases but it doesn't work with std::vector<double>. This is the way how I declare this conversion in my boost python module as:

BOOST_PYTHON_MODULE(libmymodule_pywrap) {
  .
  .
  .
  bp::to_python_converter<std::vector<double, std::allocator<double> >,
                          vector_to_list<double, false>, true>(); // this doesn't work
  bp::to_python_converter<std::vector<Eigen::VectorXd,
                          std::allocator<Eigen::VectorXd> >,
                          vector_to_list<Eigen::VectorXd, false>, true>(); // this works well
}

And I get the following compilation error:

/usr/include/boost/python/object/make_instance.hpp:27:9: error: no matching function for call to ‘assertion_failed(mpl_::failed************ boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >::************)’
         BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));
         ^
/usr/include/boost/mpl/assert.hpp:83:5: note: candidate: template<bool C> int mpl_::assertion_failed(typename mpl_::assert<C>::type)
 int assertion_failed( typename assert<C>::type );
     ^
/usr/include/boost/mpl/assert.hpp:83:5: note:   template argument deduction/substitution failed:
/usr/include/boost/python/object/make_instance.hpp:27:9: note:   cannot convert ‘mpl_::assert_arg<boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> > >(0u, 1)’ (type ‘mpl_::failed************ boost::mpl::or_<boost::is_class<double>, boost::is_union<double>, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >::************’) to type ‘mpl_::assert<false>::type {aka mpl_::assert<false>}’
         BOOST_MPL_ASSERT((mpl::or_<is_class<T>, is_union<T> >));

Do somebody understand what it's going on?

  • Why not use `vector_indexing_suite`? : `class_>("vecDouble") .def(vector_indexing_suite >())` ; – jackw11111 Jul 02 '20 at 07:37
  • I am aware of this approach, but I decided to do not follow it since it is not Pythonic. – Carlos Mastalli Jul 03 '20 at 09:17
  • Agreed, it isn't as Pythonic but it with wrappers, it seems to take a bit of effort to get a similar ease of use. Maybe this (https://stackoverflow.com/a/15940413/9238288) answer might give you some info. Goodluck! – jackw11111 Jul 03 '20 at 09:27

1 Answers1

0

I am learning Boost::Python as well and unfortunately don't understand how to solve that error, but this example seems to avoid the error message, which you may be able to modify to your own needs.

template<typename T>
struct vector_to_list
{
    static PyObject* convert(const std::vector<T>& src)
    {
        boost::python::list result;
        for (int i = 0; i < src.size(); i++)
        {
            result.append(src[i]);
        }

        return boost::python::incref(result.ptr());
    }
};
...
boost::python::to_python_converter<std::vector<double>, vector_to_list<double> >();
...

However, if this is to provide functionality like, for example:

getData() is declared in C++:

vector<double> getData() { return m_Data; }

where, for example, vector<double> m_Data = {1.0, 2.0, 3.0}; and you wanted in Python:

data = example.getData()
print (data)

[1.0, 2.0, 3.0]

You could implement it by creating a generic container and register each like this (courtesy of this answer):

/// @brief Type that allows for registration of conversions from
///        python iterable types.
struct iterable_converter
{
  /// @note Registers converter from a python interable type to the
  ///       provided type.
  template <typename Container>
  iterable_converter&
  from_python()
  {
    boost::python::converter::registry::push_back(
      &iterable_converter::convertible,
      &iterable_converter::construct<Container>,
      boost::python::type_id<Container>());

    // Support chaining.
    return *this;
  }

  /// @brief Check if PyObject is iterable.
  static void* convertible(PyObject* object)
  {
    return PyObject_GetIter(object) ? object : NULL;
  }

  /// @brief Convert iterable PyObject to C++ container type.
  ///
  /// Container Concept requirements:
  ///
  ///   * Container::value_type is CopyConstructable.
  ///   * Container can be constructed and populated with two iterators.
  ///     I.e. Container(begin, end)
  template <typename Container>
  static void construct(
    PyObject* object,
    boost::python::converter::rvalue_from_python_stage1_data* data)
  {
    namespace python = boost::python;
    // Object is a borrowed reference, so create a handle indicting it is
    // borrowed for proper reference counting.
    python::handle<> handle(python::borrowed(object));

    // Obtain a handle to the memory block that the converter has allocated
    // for the C++ type.
    typedef python::converter::rvalue_from_python_storage<Container>
                                                                storage_type;
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes;

    typedef python::stl_input_iterator<typename Container::value_type>
                                                                    iterator;

    // Allocate the C++ type into the converter's memory block, and assign
    // its handle to the converter's convertible variable.  The C++
    // container is populated by passing the begin and end iterators of
    // the python object to the container's constructor.
    new (storage) Container(
      iterator(python::object(handle)), // begin
      iterator());                      // end
    data->convertible = storage;
  }
};

BOOST_PYTHON_MODULE(example)
{
    // Register interable conversions.
    iterable_converter()
    .from_python<std::vector<double> > ()
    .from_python<std::vector<Eigen::VectorXd> >()    
    ;
}

Which allows for chaining, nested vectors and an API that is more Pythonic than with indexed_vector_suite cases like:

data = example.doubleVector()
data[:] = example.getData()

you can simply use:

data = example.getData()
jackw11111
  • 1,457
  • 1
  • 17
  • 34