[C++-sig] Moving "decorator" into C++ part of module

Albert Strasheim fullung at gmail.com
Mon May 21 16:02:07 CEST 2007


Howdy

On Mon, 21 May 2007, Neal Becker wrote:

> Albert Strasheim wrote:
> 
> > Hello all
> > 
> > I have a class with various methods that return uBLAS matrices. I've
> > wrapped various instantiations of these matrices with:
> > 
> [...]
> 
> I'm very interested in this.
> 
> I thought array_interface was only a proposal at this point.  You are using
> it?  If I have python2.5, do I need something to try this out?  (Like,
> where is the array_interface include file?)

NumPy's array interface is ready to go. I think you might be thinking 
about the enhanced buffer protocol PEP thing.

> Could you post a complete code that shows how you convert ublas::matrix to
> numpy?  Do you have converters in the other direction?

Code attached. This is still very much a work in progress. Test it with:

import numpy as N
import os

from numpy.testing import *
# directory where you compiled the module
set_local_path(os.path.join('..', 'win_build', 'Debug'))
import pyublas
restore_path()

at = pyublas.array_test()
print at
print at.ref_return()
print at.const_ref_return()
print at.pointer_return()
print at.const_pointer_return()

As you can see, I can now convert return types (just haven't implemented 
return by-value yet). Now that I understand call policies properly, I'm 
going to look at converting arguments.

Cheers,

Albert
-------------- next part --------------
#include <boost/numeric/ublas/matrix.hpp>

#include <boost/python.hpp>



#define PYUBLAS_FILE_WITH_INIT

#ifndef PYUBLAS_FILE_WITH_INIT

#define NO_IMPORT_ARRAY

#endif

#include <numpy/arrayobject.h>



// XXX for debugging purposes... remove later

#include <boost/numeric/ublas/io.hpp>

#include <iostream>



namespace ublas = boost::numeric::ublas;

namespace py = boost::python;



template<typename T>

struct matrix_wrapper

{

    static void wrap(const char* python_name)

    {

        py::class_<T, boost::noncopyable>(python_name, py::no_init)

            // This will have to copy for things like symmetric matrices

            .add_property("__array_struct__", array_struct__)

            ;

        // register conversion for value arguments

        py::to_python_converter<T, matrix_wrapper<T> >();

        // return conversion for return by-value

        py::converter::registry::push_back(convertible, construct, py::type_id<T>());

    }



    static void cleanup(void* obj)

    {

        PyArrayInterface* ai = (PyArrayInterface*) obj;

        delete [] ai->shape;

        delete [] ai->strides;

        delete ai;

    }



    static PyObject* array_struct__(T const& self)

    {

        // http://numpy.scipy.org/array_interface.shtml

        PyArrayInterface* ai = new PyArrayInterface;



        // contains the integer 2 as a sanity check

        ai->two = 2;

        // number of dimensions: two for a matrix

        ai->nd = 2;

        // kind in array --- character code of typestr

        ai->typekind = 'f';

        // size of each element

        ai->itemsize = sizeof(T::value_type);



        // how should be data interpreted

        ai->flags = NPY_CONTIGUOUS | NPY_ALIGNED | NPY_NOTSWAPPED | NPY_WRITEABLE;



        // A length-nd array of shape information

        ai->shape = new npy_intp[2];

        ai->shape[0] = self.size1();

        ai->shape[1] = self.size2();



        // A length-nd array of stride information

        ai->strides = new npy_intp[2];

        ai->strides[0] = ai->shape[1] * ai->itemsize;

        ai->strides[1] = ai->itemsize;



        // A pointer to the first element of the array

        ai->data = (void*) self.data().begin();



        ai->descr = NULL;



        return PyCObject_FromVoidPtr(ai, cleanup);

    }



    static void* convertible(PyObject* obj) {

        return PyArray_Check(obj) ? obj : NULL;

    }



    static void construct(PyObject* obj, py::converter::rvalue_from_python_stage1_data* data)

    {

        // get the storage

        typedef py::converter::rvalue_from_python_storage<T> storage_t;

        storage_t* the_storage = reinterpret_cast<storage_t*>(data);

        void* memory_chunk = the_storage->storage.bytes;

        // placement new

        T* a = new (memory_chunk) T(0, 0);   // TODO need a special array adaptor here...

        data->convertible = memory_chunk;

    }



    static PyObject* convert(const T& a) {

        // TODO still need to do some work here

        return py::incref(Py_None);

    }

};



typedef ublas::matrix<double, ublas::row_major, ublas::array_adaptor<double> > matrix_type;



struct array_test

{

    typedef matrix_type array_t;

    array_test() : data_(3, 4)

    {

        for (unsigned i = 0; i < data_.size1(); ++i) {

            for (unsigned j = 0; j < data_.size2(); ++j) {

                data_(i, j) = 3 * i + j;

            }

        }

    }

    array_t value_return() const

    {

        return data_;

    }

    array_t const* const_pointer_return() const

    {

        return &data_;

    }

    array_t* pointer_return()

    {

        return &data_;

    }

    array_t const& const_ref_return() const

    {

        return data_;

    }

    array_t& ref_return()

    {

        return data_;

    }

    void value_arg(array_t a)

    {

        print(a);

    }

    void pointer_arg(array_t* a)

    {

        print(*a);

    }

    void const_pointer_arg(array_t const* a)

    {

    }

    void ref_arg(array_t& a)

    {

    }

    void const_ref_arg(array_t const& a)

    {

    }

private:

    void print(array_t const& a)

    {

        std::cout << a << std::endl;

    }



    array_t data_;

};



template<class BasePolicy_ = default_call_policies>

struct return_asarray : BasePolicy_

{

    template <class ArgumentPackage>

    static PyObject* postcall(ArgumentPackage const& args_, PyObject* result)

    {

        result = BasePolicy_::postcall(args_, result);

        if (result == 0) {

            return 0;

        }

        return PyArray_FromStructInterface(result);

    }

};



BOOST_PYTHON_MODULE(pyublas)

{

    import_array();



    matrix_wrapper<matrix_type>::wrap("matrix_double");



    py::class_<array_test, boost::noncopyable>("array_test")

        .def("value_return", &array_test::value_return)

        .def("const_pointer_return", &array_test::const_pointer_return, return_asarray<py::return_internal_reference<> >())

        .def("pointer_return", &array_test::pointer_return, return_asarray<py::return_internal_reference<> >())

        .def("const_ref_return", &array_test::const_ref_return, return_asarray<py::return_internal_reference<> >())

        .def("ref_return", &array_test::ref_return, return_asarray<py::return_internal_reference<> >())

#if 0

        .def("value_arg", &array_test::value_arg)

        .def("pointer_arg", &array_test::pointer_arg)

        .def("const_pointer_arg", &array_test::const_pointer_arg)

        .def("ref_arg", &array_test::ref_arg)

        .def("const_ref_arg", &array_test::const_ref_arg)

#endif

        ;

}



More information about the Cplusplus-sig mailing list