Я пытаюсь разоблачить eigen3 в python, используя Boost.Python.
Я не могу найти способ разоблачить функцию unaryExpr (const CustomUnaryOp &func=CustomUnaryOp())
То, что я хотел бы иметь, - это то, что позволяет мне что-то вроде этого:
питон
import libMatrix as mat
a = mat.Matrix(10, 10)
mat.unary_expr( lambda x : 1)
Есть ли у вас какие-либо идеи?? Это может выглядеть так:
void unary_expr(Matrix const& self, PyObject* callable_object)
{
cpp_callable = ??(callable_object)
self.unaryEpxr(cpp_callable);
}
=== Что я пробовал: ==========================================
1) Я попытался использовать простое определение обратного вызова
typedef double(*UnaryExprType)(double);
void unary_expr(Matrix const& self, UnaryExprType a);
{
self.unaryEpxr( a );
}
но boost не преобразует функцию python в UnaryExprType.
2) Я попытался реализовать структуру PythonCallBack
, тем не менее, она не работает, у меня есть ошибка, что подпись python не соответствует сигнатуре c++.
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 );
}
Сообщение об ошибке
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))
Boost.Python предназначен для минимизации необходимости взаимодействия с PyObject
, и часто можно просто использовать boost::python::object
таким же образом, как и с объектом в Python. Например, если func
- это boost::python::object
который ссылается на lambda x: 1
, то здесь используется следующее использование Boost.Python с комментариями на комментарии Python:
// >>> 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));
В этом случае, поскольку код C++, скорее всего, ожидает, что возвращающее значение функтора будет иметь тип C++, а не общий boost::python::object
, можно использовать тип оболочки для адаптации функтора.
/// @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_;
};
Вот полный минимальный пример:
#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)
;
}
Интерактивное использование:
>>> import example
>>> matrix = example.Matrix()
>>> matrix.value = 21
>>> assert(21 == matrix.value)
>>> matrix.unaryExpr(lambda x: x*2)
>>> assert(42 == matrix.value)