[C++-sig] Simple Boost.Python and NumPy example--help me make it better!

Christopher A Mejia camejia at raytheon.com
Fri Jun 26 15:48:00 CEST 2009


Hi,

I've been trying to do something that is I would think is a very common
task: I'm trying to expose to Python a C++ class with a member function that
inputs an array of data.  On the Python side, I would like to use NumPy to
store this array of data.  I've spent many hours searching online for a
simple example, but haven't found one.  So, I've finally gotten my own 
simple
example working, but I'm not sure I'm handling everything correctly, and I'm
sure there's room for improvement...so, I'm posting my example here in the
hopes that this group can help me make it better.  My goal here is to have
short and simple code, at the expense of performance.  For example, I'm
willing to explicitly copy my data array, if that's what I need to do to
make use of existing libraries.

The files array_test.hpp and array_test.cpp below contain the class I
would like to expose to Python.  Note that the member function sum() expects
a pointer to a double array and an integer for the lengh of the array.  The
files array_test2.hpp and array_test2.cpp contain a derived class which
inputs a boost::python::numeric::array.  The file array_test_ext.cpp uses
Boost.Python to expose these classes to Python.  Everything appears to be
working (the final file array_test.py gives the answer I expect) but I
still think there's room for improvement.  Here are some questions I have:

1.  Is it necessary for me to define a whole new derived class just to add
the extra layer on top of the sum() member function?  I'm guessing that 
adding
another layer on top of an existing member function must be a pretty common
usage pattern so I'm wondering if there's a cleaner way to do it.

2. What is the best way (and the correct syntax) to pass the
boost::python::numeric::array into the member function?  Right now, I'm just
using the syntax for a simple pass-by-value, but maybe I should use
pass-by-reference?  And should I put a "const" somewhere?

3.  I don't think it should be necessary to pass the length of the data
array into my function.  The boost::python::numeric::array class has an
nelements() member function.  When I use the nelements() member function, my
code compiles, but I get a Python runtime error that NumPy ndarrays don't
have an nelements() attribute.  What is the correct way to extract the
length of the data array from the NumPy array?

4. I must admit that I don't understand <extract>.  Is there a more
efficient way to copy the data from my Python NumPy array into the C++
double * array?

Any answers or suggestions for improvement would be most welcome!

--Chris

// array_test.hpp

#ifndef _ARRAY_TEST
#define _ARRAY_TEST

class array_test
{
public:
  double mysqr(double x);
  double sum(double *x, int n);
};

#endif

// array_test.cpp

#include "array_test.hpp"

double array_test::mysqr(double x)
{
  return(x * x);
}

double array_test::sum(double *x, int n)
{
  double thesum = 0.;
  for(int i = 0; i < n; i++)
    {
      thesum += x[i];
    }
  return(thesum);
}

// array_test2.hpp

#include <boost/python/list.hpp>
#include <boost/python/numeric.hpp>
#include "array_test.hpp"

class array_test2 : public array_test
{
public:
  double sum(boost::python::list o);
  double sum(boost::python::numeric::array y, int n);
};

// array_test2.cpp

#include <boost/python/extract.hpp>
#include "array_test2.hpp"

double array_test2::sum(boost::python::list o)
{
  std::size_t n = len(o);
  double *tmp = new double[n];
  for(unsigned int i = 0; i < n; i++)
    {
      tmp[i] = boost::python::extract<double>(o[i]);
    }
  double thesum = array_test::sum(tmp, n);
  delete tmp;
  return(thesum);
}

double array_test2::sum(boost::python::numeric::array y, int n)
{
  double *tmp = new double[n];
  for(int i = 0; i < n; i++)
    {
      tmp[i] = boost::python::extract<double>(y[i]);
    }
  double thesum = array_test::sum(tmp, n);
  delete tmp;
  return(thesum);
}

// array_test_ext.cpp

// This file has been generated by Py++. (and then edited...)

#include "boost/python.hpp"

#include "/home/cmejia/Python/boost/array_test.hpp"
#include "/home/cmejia/Python/boost/array_test2.hpp"

namespace bp = boost::python;

BOOST_PYTHON_MODULE(array_test_ext){
  bp::numeric::array::set_module_and_type("numpy", "ndarray");
    bp::class_< array_test >( "array_test" )
        .def(
            "mysqr"
            , (double ( ::array_test::* )( double ) )( 
&::array_test::mysqr )
            , ( bp::arg("x") ) )
        .def(
            "sum"
            , (double ( ::array_test::* )( double *,int ) )( 
&::array_test::sum )
            , ( bp::arg("x"), bp::arg("n") ) );
    bp::class_< array_test2, bp::bases< array_test > >( "array_test2" )
        .def(
            "sum"
            , (double ( ::array_test2::* )( bp::list ) )( 
&::array_test2::sum )
            , ( bp::arg("o") ) )
        .def(
            "sum"
            , (double ( ::array_test2::* )( bp::numeric::array,int ) )( 
&::array_test2::sum )
            , ( bp::arg("y"), bp::arg("n") ) );
}



More information about the Cplusplus-sig mailing list