[C++-sig] register_boost_array_from_python

Ralf W. Grosse-Kunstleve rwgk at yahoo.com
Sat Jun 1 18:35:03 CEST 2002


> callback_from_python takes possession of its argument just like reference<>
> does. It looks like you're dropping a reference in the transfer from the
> tuple.

callback_from_python does not accept increment_count like reference<>
does. Are you suggesting that I use Py_INCREF directly?

> No, it's not. I think that's strictly a v1 idiom. We could consider
> reinstating it as an option, but I don't think it would play well with the
> more-sophisticated mechanism for overload resolution that I have planned.

I am happy with your approach.

> Well, yes. data->convertible is set initially to the result of your
> convertible() function. If that returns 0, the function isn't considered a
> candidate for overload resolution and the convert function is never called.
> As I said earlier, once the convert function is called, the only way to
> signal failure is with an exception.

Understood.

> How about a STL-container_from_something-which-supports-iter() converter?
> That's what people really need.

Sounds great. As it stands (new code below) I am iterating over the
sequence twice: first in convertible() and then again in construct().
Do all Python iterators support this?

Hm, if data->convertible is not 0, will construct() be called without
further preconditions? If so, I could manage my own memory where I
store the result of iterating over the input sequence. Can I then
simply assign ownership in construct() (to avoid a copy)?
Or, would it make sense to add, say, rvalue_stage0_data* to the
signature of convertible()?

About the code below: there are now three array adaptors that I have
stress-tested (so far Linux/gcc only) with boost::array, std::vector and
the "small" type of my array family which is similar to the
"fixed_capacity_vector" proposed a while ago by Synge Todo.
Example usage:

  bpl_utils::register_array_from_python_list_or_tuple<
    boost::array<double, 3>,
    bpl_utils::fixed_size_array_registration_adaptor>();

  bpl_utils::register_array_from_python_list_or_tuple<
    std::vector<double>,
    bpl_utils::variable_size_array_registration_adaptor>();

  bpl_utils::register_array_from_python_list_or_tuple<
    af::small<double, 6>,
    bpl_utils::fixed_capacity_array_registration_adaptor>();

If you are interested I'd be happy to contribute this +
improvements/fixes that you might suggest + documentation to the core
library.

Ralf


  struct fixed_size_array_registration_adaptor
  {
    template <typename ArrayType>
    static bool check_size(boost::python::type<ArrayType>, std::size_t sz)
    {
      return ArrayType::size() == sz;
    }

    template <typename ArrayType>
    static void reserve(ArrayType& a, std::size_t sz)
    {
      assert(check_size(a, sz));
    }

    template <typename ArrayType, typename ValueType>
    static void set_value(ArrayType& a, std::size_t i, ValueType const& v)
    {
      a[i] = v;
    }
  };

  struct variable_size_array_registration_adaptor
  {
    template <typename ArrayType>
    static bool check_size(boost::python::type<ArrayType>, std::size_t sz)
    {
      return true;
    }

    template <typename ArrayType>
    static void reserve(ArrayType& a, std::size_t sz)
    {
      a.reserve(sz);
    }

    template <typename ArrayType, typename ValueType>
    static void set_value(ArrayType& a, std::size_t i, ValueType const& v)
    {
      assert(a.size() == i);
      a.push_back(v);
    }
  };

  struct fixed_capacity_array_registration_adaptor
    : variable_size_array_registration_adaptor
  {
    template <typename ArrayType>
    static bool check_size(boost::python::type<ArrayType>, std::size_t sz)
    {
      return ArrayType::max_size() >= sz;
    }
  };

  template <typename ArrayType, typename ArrayAdaptor>
  struct register_array_from_python_list_or_tuple
  {
    typedef typename ArrayType::value_type array_element_type;

    register_array_from_python_list_or_tuple()
    {
      boost::python::converter::registry::insert(
        &convertible,
        &construct,
        boost::python::type_id<ArrayType>());
    }

    static void* convertible(PyObject* obj)
    {
      using namespace boost::python;
      tuple t = list_or_tuple_as_tuple(obj);
      if (!ArrayAdaptor::check_size(type<ArrayType>(), t.size())) return 0;
      for(std::size_t i=0;i<t.size();i++) {
        from_python<array_element_type const&> from_py(t[i].get());
        if (!from_py.convertible()) return 0;
      }
      return obj;
    }

    static void construct(
      PyObject* obj, boost::python::converter::rvalue_stage1_data* data)
    {
      using namespace boost::python;
      tuple t = list_or_tuple_as_tuple(obj);
      void* storage = ((
        converter::rvalue_base_data<ArrayType>*)data)->storage.bytes;
      new (storage) ArrayType();
      data->convertible = storage;
      ArrayType& result = *((ArrayType*)storage);
      ArrayAdaptor::reserve(result, t.size());
      for(std::size_t i=0;i<t.size();i++) {
#if 0
        result[i] = converter::callback_from_python<
          array_element_type>()(t[i].get());
#else
        from_python<array_element_type const&> from_py(t[i].get());
        ArrayAdaptor::set_value(result, i, from_py(t[i].get()));
#endif
      }
    }

    private:
      static boost::python::tuple list_or_tuple_as_tuple(PyObject* obj)
      {
        using namespace boost::python;
        tuple result;
        if      (PyList_Check(obj)) {
          result = tuple(ref(PyList_AsTuple(obj)));
        }
        else if (PyTuple_Check(obj)) {
          result = tuple(ref(obj, ref::increment_count));
        }
        return result;
      }
  };


__________________________________________________
Do You Yahoo!?
Yahoo! - Official partner of 2002 FIFA World Cup
http://fifaworldcup.yahoo.com





More information about the Cplusplus-sig mailing list