8

我正在尝试使用 c++11 lambdas 作为boost::python's中的访问器函数add_property,如下所示(此示例中并不严格需要 lambda,但对于 lambda 内部发生的更复杂的事情(例如验证)将需要它):

#include<boost/python.hpp>

struct A{
  A(): a(2){};
  int a;
};

BOOST_PYTHON_MODULE(boost_python_lambda)
{
  boost::python::class_<A>("A")
    // .def_readonly("a",&A::a) // the classical way: works fine 
    .add_property("a",[](const A& a){return a.a;})
  ;
}

但是,使用 clang++ (ver. 3.2) 和-std=c++11(结果与 g++ 4.7 相同) 编译,我得到这个错误:

/usr/include/boost/python/class.hpp:442:66: error: no matching function for call to 'get_signature'
        return python::make_function(f, default_call_policies(), detail::get_signature(f, (T*)0));
                                                                 ^~~~~~~~~~~~~~~~~~~~~
/usr/include/boost/python/class.hpp:422:22: note: in instantiation of function template specialization 'boost::python::class_<A,
      boost::python::detail::not_specified, boost::python::detail::not_specified,
      boost::python::detail::not_specified>::make_fn_impl<A, <lambda at boost_python_lambda.cpp:12:21> >' requested here
        return this->make_fn_impl(
                     ^
/usr/include/boost/python/class.hpp:309:40: note: in instantiation of function template specialization 'boost::python::class_<A,
      boost::python::detail::not_specified, boost::python::detail::not_specified,
      boost::python::detail::not_specified>::make_getter<<lambda at boost_python_lambda.cpp:12:21> >' requested here
        base::add_property(name, this->make_getter(fget), docstr);
                                       ^
boost_python_lambda.cpp:12:4: note: in instantiation of function template specialization 'boost::python::class_<A,
      boost::python::detail::not_specified, boost::python::detail::not_specified,
      boost::python::detail::not_specified>::add_property<<lambda at boost_python_lambda.cpp:12:21> >' requested here
                .add_property("a",[](const A& a){return a.a;})
                 ^

我尝试将 lambda 包装在 中std::function<int(const A&)>(...),但这对参数推导没有帮助。任何想法?

4

4 回答 4

11

两年后跳到这里,Boost.Python 确实不支持包装函数对象。但是你的 lambda 没有捕获任何东西。因此,它可以显式转换为函数指针:

BOOST_PYTHON_MODULE(boost_python_lambda)
{
  boost::python::class_<A>("A")
    // .def_readonly("a",&A::a) // the classical way: works fine 
    .add_property("a", +[](const A& a){return a.a;})
                       ↑↑↑
  ;
}

你所需要的就是那个+

于 2015-06-11T21:05:55.553 回答
5

使用该make_function()函数创建 Python 可调用对象。如果 Boost.Python 无法推导出函数对象签名,则必须将签名显式提供为MPL 前端可扩展序列。例如,lambda[](const A& a) { return a.a; }返回一个int并接受const A&,因此可以boost::mpl::vector<int, const A&>()用于签名。

这是一个完整的示例,演示了使用指向数据成员的指针、将非捕获 lambda 转换为函数以及使用 lambda/functor:

#include <boost/python.hpp>

struct A
{
  A(): a(2) {};
  int a;
};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<A>("A")
    // Expose pointer-to-data-member.
    .add_property("a1", &A::a)
    // Cast non-capturing lambda to a function.
    .add_property("a2", +[](const A& a) { return a.a; })
    // Make a python function from a functor.
    .add_property("a3", python::make_function(
        [](const A& a) { return a.a; },
        python::default_call_policies(),
        boost::mpl::vector<int, const A&>()))
    ;
}

交互使用:

>>> import example
>>> a = example.A()
>>> assert(a.a1 == 2)
>>> assert(a.a2 == 2)
>>> assert(a.a3 == 2)

另一种基于记录行为的非侵入式方法是将 lambda 编写为非成员函数,然后将其作为fget参数公开。虽然不如 lambda 简洁,但它仍然允许在访问成员变量时发生其他功能,例如验证。

#include <boost/python.hpp>

struct A{
  A(): a(2){};
  int a;
};

int get_a(const A& a)
{
  // do validation
  // do more complicated things
  return a.a;
}

BOOST_PYTHON_MODULE(example)
{
  boost::python::class_<A>("A")
    .add_property("a", &get_a);
  ;
}
于 2013-05-31T13:16:53.627 回答
4

我在尝试使用 lambda 时遇到了同样的问题,基于上面的解决方案std::function,我添加了更多的模板魔法,推导出operator()lambda(或仿函数)的成员函数的类型:

https://gist.github.com/YannickJadoul/013d3adbf7f4e4abb4d5

当包含此标头时,这样的事情就会起作用:

int *x = new int(0);
def("inc", [x] () { ++*x; });
def("get", [x] () { return *x; });

我所知道的唯一警告是,ftm,您应该在包含之前包含此标头boost/python.hpp(或您正在使用的任何其他 Boost.Python 标头,需要get_signature函数声明):

#include "functor_signature.h"
#include <boost/python.hpp>
于 2015-11-27T14:36:17.593 回答
3

如果您通过创建 std::function 使函数类型显式,则可以使用以下 (C++11) 代码来执行此操作

namespace boost {
  namespace python {
    namespace detail {

      template <class T, class... Args>
      inline boost::mpl::vector<T, Args...> 
        get_signature(std::function<T(Args...)>, void* = 0)
      {
        return boost::mpl::vector<T, Args...>();
      }

    }
  }
}

例子:

boost::python::class_<A>("A")
    // .def_readonly("a",&A::a) // the classical way: works fine 
    // .add_property("a", [](const A& a){return a.a; }) // ideal way, not possible since compiler cannot deduce return / arguments types
    .add_property("a", std::function<int(const A&)>([](const A& a){return a.a; }))
    ;
于 2014-08-13T08:54:26.190 回答