0

I have written a small class method which calls a python function from C++ via boost::python and I already observe nice speed improvements wrt pure python:

namespace bp = boost::python;
double pyf::eval(double * const x) const
{
  bp::list lx;
  for (size_t i = 0; i < this->get_n(); i++)
    lx.append(x[i]);
  return bp::call<double>(pycb_, lx);
}

pycb_ is my PyObject* callback function.

My question is: do you know a smarter way of creating a boost::python::list from a double * pointer ? My current solution is quite dummy, but I find the boost::python documentation hard to understand and lacking concrete examples, so your experience is welcome !

For info, I'm using a relatively old version of Boost (1.41). Also, the rest of my package relying on boost, I would prefer to stick to boost::python and avoid adding third party pkgs.

Mr Renard
  • 43
  • 4
  • I would suggest a NumPy array rather than a list. You can convert between NumPy array and list if you need to. – CashCow Nov 18 '13 at 15:27
  • You can create the NumPy array like the answer here: http://stackoverflow.com/questions/10701514/how-to-return-numpy-array-from-boostpython/14232897#14232897 – CashCow Nov 18 '13 at 15:33
  • Thanks, I tried, but I got a crash, so I need to dig further if I really need an `ndarray` instead of a `tuple`. For the moment, a `tuple` is enough for my needs. – Mr Renard Nov 19 '13 at 10:27
  • If you got a crash it doesn't mean it's a bad solution, just that you didn't do it properly so introduced a bug. – CashCow Nov 19 '13 at 11:41

1 Answers1

0

As far as I know, iteration is the only way to populate a Boost.Python list from that type of source. Alternatives iterative solutions the remove some of the boilerplate code include using Boost.ForEach or the std::for_each algorithm.

For example, the following two snippets are equivalent to the original code:

double pyf::eval(double * const x) const
{
  bp::list lx;
  BOOST_FOREACH(double& value, boost::make_iterator_range(x, x + this->get_n()))
    lx.append(value);
  return bp::call<double>(pycb_, lx);
}

and

double pyf::eval(double * const x) const
{
  bp::list lx;
  std::for_each(x, x + this->get_n(), 
                boost::bind(&bp::list::append<double>, &lx, _1));
  return bp::call<double>(pycb_, lx);
}

Here is a basic example with an embedded Python showing both approaches:

#include <algorithm>
#include <iostream>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/python.hpp>

int main()
{
  Py_Initialize();

  namespace python = boost::python;
  try
  {
    python::object main = python::import("__main__");

    // Add the verbose_sum function to main.
    python::exec(
      "def verbose_sum(x):\n"
      "    print x\n"
      "    return sum(x)\n"
      "\n", main.attr("__dict__"));

    // Mockup data.
    PyObject* callback = python::object(main.attr("verbose_sum")).ptr();
    boost::array<double, 5> x_array = {{ 1, 2, 3, 4, 5 }};
    double* const x = &x_array[0];
    const std::size_t n = x_array.size();

    // Range iterator.
    {
      python::list x_list;
      BOOST_FOREACH(double& value, boost::make_iterator_range(x, x + n))
        x_list.append(value);
      std::cout << python::call<double>(callback, x_list) << std::endl;
    }

    // Algorithm with binding.
    {
      python::list x_list;
      std::for_each(x, x + n, 
                    boost::bind(&python::list::append<double>, &x_list, _1));
      std::cout << python::call<double>(callback, x_list) << std::endl;
    }
  }
  catch (python::error_already_set&)
  {
    PyErr_Print();
  }
}

And the output:

[1.0, 2.0, 3.0, 4.0, 5.0]
15
[1.0, 2.0, 3.0, 4.0, 5.0]
15
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169