Using C ++ 11 lambda as an access function in boost :: python add_property (get_signature doesn't work with lambda)

I am trying to use lambdas C ++ 11 as an access function in boost::python add_property , something like the following (in this example, lambda is not strictly needed, but will be needed for more complex things happening inside lambda, for example, checking):

 #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 aa;}) ; } 

However compiling with clang ++ (version 3.2) and -std=c++11 (the result is the same with g ++ 4.7), I get this error:

 /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 aa;}) ^ 

I tried wrapping the lambda in std::function<int(const A&)>(...) , but this did not help to output the argument. Any idea?

+6
c ++ lambda c ++ 11 boost-python
source share
4 answers

Jumping here after two years, Boost.Python really doesn't support wrapper function objects. But your lambda doesn’t capture anything. Thus, it can be explicitly converted to a function pointer:

 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 aa;}) ↑↑↑ ; } 

All you need is + .

+7
source share

Use the make_function() function to create objects called by Python. If Boost.Python cannot infer the signature of the function object, then the signature must be explicitly provided as an MPL front-extensible sequence . For example, lambda [](const A& a) { return aa; } [](const A& a) { return aa; } returns int and accepts const A& , so boost::mpl::vector<int, const A&>() can be used for signing.

Here is a complete example demonstration using a data pointer element, casting non-capture lambda into a function and using 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 aa; }) // Make a python function from a functor. .add_property("a3", python::make_function( [](const A& a) { return aa; }, python::default_call_policies(), boost::mpl::vector<int, const A&>())) ; } 

Interactive use:

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

An alternative non-intrusive approach based on documented behavior is to write lambda as a non-member function and then expose it as an fget argument. Although not as concise as lambda, it still allows you to use additional features, such as validation, when accessing a member variable.

 #include <boost/python.hpp> struct A{ A(): a(2){}; int a; }; int get_a(const A& a) { // do validation // do more complicated things return aa; } BOOST_PYTHON_MODULE(example) { boost::python::class_<A>("A") .add_property("a", &get_a); ; } 
+3
source share

If you create a function type explicitly by creating a std :: function, then using the following code fragment (C ++ 11), you can do this

 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...>(); } } } } 

Example:

 boost::python::class_<A>("A") // .def_readonly("a",&A::a) // the classical way: works fine // .add_property("a", [](const A& a){return aa; }) // ideal way, not possible since compiler cannot deduce return / arguments types .add_property("a", std::function<int(const A&)>([](const A& a){return aa; })) ; 
+3
source share

I faced the same problem when trying to use lambda and based on the solution for std::function above, I added some more templates, deducing the type of function of the member operator() lambda (or functor):

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

And then such things just work when this header is included:

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

The only caveat I know of ftm is that you must include this header, including boost/python.hpp (or any other Boost.Python header that you use, this requires get_signature function get_signature ):

 #include "functor_signature.h" #include <boost/python.hpp> 
+3
source share

All Articles