0

I am trying to expose eigen3 in python using Boost.Python.

I cannot find a way to expose the function unaryExpr (const CustomUnaryOp &func=CustomUnaryOp())

What I would like to have is something that allow me to something like that:

python

import libMatrix as mat
a = mat.Matrix(10, 10)
mat.unary_expr( lambda x : 1)

Do you have any idea ?? It may look like that:

void unary_expr(Matrix const& self, PyObject* callable_object)
{
   cpp_callable = ??(callable_object)
   self.unaryEpxr(cpp_callable);
}

=== What I tried: ==========================================

1) I tried to use a simple callback definition

typedef double(*UnaryExprType)(double);
void unary_expr(Matrix const& self, UnaryExprType a);
    {
       self.unaryEpxr( a );
    }

but boost does not convert the python function into a UnaryExprType.

2) I tried to implement a struct PythonCallBack, Nevertheless, it does not work, I have got an error that the python signature did not match the c++ signature.

struct PythonCallBackBase
{
public:
    virtual ~PythonCallBackBase()   {}
    virtual double operator() (double const & x) {  return 0;   }
};

struct PythonCallBack : PythonCallBackBase, boost::python::wrapper<PythonCallBackBase>
{
public:
    typedef boost::python::wrapper<PythonCallBackBase> wrap;

    double default_op(double const & x)
    {
        return 0;
    }

    double operator() (double const & x)
    {
        if (boost::python::override f = wrap::get_override("__call__"))
            return f(x);
        return PythonCallBackBase::operator ()(x);
    }
};

void unary_expr(Matrix const& self, PythonCallBack a)
{
   self.unaryEpxr( a );
}

Error message

ArgumentError: Python argument types in
    Matrix.unary_expr(Matrix, Boost.Python.class)
did not match C++ signature:
    unary_expr(Eigen::Matrix<double, -1, -1, 0, -1, -1>, PythonCallBack)
    unary_expr(Eigen::Matrix<double, -1, -1, 0, -1, -1>, double (*)(double))
1

1 Answer 1

1

Boost.Python is designed to minimize the need to interact with PyObject, and one can often simple use boost::python::object in the same manner they would with an object in Python. For instance, if func is a boost::python::object that refers to a lambda x: 1, then here is the following Boost.Python usage with annotated Python comments:

// >>> func = lambda x: 1
boost::python::object func = ...;
// >>> result = func(42)
boost::python::object result = func(42);
// >>> assert(1 == result)
assert(1 == boost::python::extract<int>(result));

In this case, as the C++ code is likely expecting for the functor's return value to be a C++ type rather than the generic boost::python::object, one can use a wrapper type to adapt the functor.

/// @brief Auxiliary type that adapts a Boost.Python object to a
///        unary functor with an explicit return type.
template <typename Arg, typename Result>
class py_unary_functor
{
public:

  typedef Arg argument_type;
  typedef Result result_type;

  py_unary_functor(boost::python::object object)
    : object_(object)
  {}

  result_type operator()(argument_type a1)
  {
    return boost::python::extract<result_type>(object_(a1))();
  }

private:
  boost::python::object object_;
};

Here is a complete minimal example:

#include <boost/python.hpp>

/// @brief Mockup matrix class.
struct matrix
{
  template <typename CustomUnaryOp>
  void unaryExpr(CustomUnaryOp func)
  {
    value = func(value);
  }

  double value;
};

/// @brief Auxiliary type that adapts a Boost.Python object to a
///        unary functor with an explicit return type.
template <typename Arg, typename Result>
class py_unary_functor
{
public:

  typedef Arg argument_type;
  typedef Result result_type;

  py_unary_functor(boost::python::object object)
    : object_(object)
  {}

  result_type operator()(argument_type a1)
  {
    return boost::python::extract<result_type>(object_(a1))();
  }

private:
  boost::python::object object_;
};

/// @brief Auxiliary function used to adapt matrix::unaryExpr.
void matrix_unaryExpr(
  matrix& self,
  boost::python::object object)
{
  py_unary_functor<double, double> func(object);
  return self.unaryExpr(func);
}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<matrix>("Matrix")
    // Expose auxiliary function.
    .def("unaryExpr", &matrix_unaryExpr)
    .add_property("value", &matrix::value, &matrix::value)
    ;
}

Interactive usage:

>>> import example
>>> matrix = example.Matrix()
>>> matrix.value = 21
>>> assert(21 == matrix.value)
>>> matrix.unaryExpr(lambda x: x*2)
>>> assert(42 == matrix.value)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.