[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