Boost.Python property definition question

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

Boost.Python property definition question

Michael Wild
Hi all

I can't seem to figure out how to wrap this in Boost.Python without the
need for auxiliary functions:


#include <string>
#include <boost/python.hpp>

using std::string;
using namespace boost::python;

class A
{
  string m_s;
public:
  A(string const& s) : m_s(s) {}
  // read-only access (
  string const& s() const { return m_s; }
  // read-write access
  string& s() { return m_s; }
};

BOOST_PYTHON_MODULE(A)
{
  class_<A> ("A", init<std::string const&>())
    .add_property("s",
      // this getter works fine
      make_function(
        (string const&(A::*)()const)&A::s,
        return_value_policy<copy_const_reference>()),
      // this setter fails
      make_function(
        (string&(A::*)())&A::s,
        return_internal_reference<>()))
    ;
}

When I try to run the following:

import A
a = A.A("Hello")
a.s = "World!"

I get below error message:

ArgumentError: Python argument types in
    None.None(A, str)
did not match C++ signature:
    None(A {lvalue})


How do I solve this problem?

Thanks for any advice


Michael
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Jim Bosch-2
On 01/30/2012 09:32 AM, Michael Wild wrote:
> Hi all
>
> I can't seem to figure out how to wrap this in Boost.Python without the
> need for auxiliary functions:
>

<snip>

>    string&  s() { return m_s; }

The second argument to add_property should be an actual setter, not a
non-const-reference getter.  In this case, you can write another free
function that delegates to the non-const-getter, then wrap that:

void set_s(A & a, std::string const & s) { a.s() = s; }

...

.add_property("s",
     make_function(
        (string const&(A::*)()const)&A::s,
        return_value_policy<copy_const_reference>()),
     make_function(&set_set)
)



HTH

Jim Bosch
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Michael Wild
On 01/30/2012 04:58 PM, Jim Bosch wrote:

> On 01/30/2012 09:32 AM, Michael Wild wrote:
>> Hi all
>>
>> I can't seem to figure out how to wrap this in Boost.Python without the
>> need for auxiliary functions:
>>
>
> <snip>
>
>>    string&  s() { return m_s; }
>
> The second argument to add_property should be an actual setter, not a
> non-const-reference getter.  In this case, you can write another free
> function that delegates to the non-const-getter, then wrap that:
>
> void set_s(A & a, std::string const & s) { a.s() = s; }
>
> ...
>
> .add_property("s",
>     make_function(
>        (string const&(A::*)()const)&A::s,
>        return_value_policy<copy_const_reference>()),
>     make_function(&set_set)
> )
>
>
>
> HTH
>
> Jim Bosch

That's what I've been referring to as "auxiliary" functions. If
possible, I'd like to avoid them because I don't fancy writing hundreds
of those... I could live with calling a template. However, so far I
haven't been able to come up with one that is sufficiently easy to use...

So far I came up with the this:

template<class ArgType, class T, T&(C::*method)()>
void set_helper(T& self, ArgType& arg)
{
  (self.*method)() = arg;
}

But it is a bit cumbersome to use, as it doesn't deduce its first two
template arguments on its own. Is there any way to achieve that feat?

Michael
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Jim Bosch-2
On 01/30/2012 12:11 PM, Michael Wild wrote:

> That's what I've been referring to as "auxiliary" functions. If
> possible, I'd like to avoid them because I don't fancy writing hundreds
> of those... I could live with calling a template. However, so far I
> haven't been able to come up with one that is sufficiently easy to use...
>
> So far I came up with the this:
>
> template<class ArgType, class T, T&(C::*method)()>
> void set_helper(T&  self, ArgType&  arg)
> {
>    (self.*method)() = arg;
> }
>
> But it is a bit cumbersome to use, as it doesn't deduce its first two
> template arguments on its own. Is there any way to achieve that feat?
>

If you're up for playing with Boost.FunctionTypes, you should be able to
write a wrapper for make_function that deduces those types for you:

namespace bp = boost::python;

template <typename A, typename T, A&(T::*method)()>
void set_helper(T & self, A const & arg) {
     (self.*method)() = arg;
}

template <class F>
bp::object make_set_helper(F f) {
     // F is the member function type; use Boost.FunctionTypes to
     // figure out A and T, then...
     return bp::make_function(&set_helper<A,T,f>);
}


I'll leave it to you to fill in the details.  I haven't tried it myself,
but I think it would work.


Jim
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Michael Wild
On 01/30/2012 07:28 PM, Jim Bosch wrote:

> On 01/30/2012 12:11 PM, Michael Wild wrote:
>> That's what I've been referring to as "auxiliary" functions. If
>> possible, I'd like to avoid them because I don't fancy writing hundreds
>> of those... I could live with calling a template. However, so far I
>> haven't been able to come up with one that is sufficiently easy to use...
>>
>> So far I came up with the this:
>>
>> template<class ArgType, class T, T&(C::*method)()>
>> void set_helper(T&  self, ArgType&  arg)
>> {
>>    (self.*method)() = arg;
>> }
>>
>> But it is a bit cumbersome to use, as it doesn't deduce its first two
>> template arguments on its own. Is there any way to achieve that feat?
>>
>
> If you're up for playing with Boost.FunctionTypes, you should be able to
> write a wrapper for make_function that deduces those types for you:
>
> namespace bp = boost::python;
>
> template <typename A, typename T, A&(T::*method)()>
> void set_helper(T & self, A const & arg) {
>     (self.*method)() = arg;
> }
>
> template <class F>
> bp::object make_set_helper(F f) {
>     // F is the member function type; use Boost.FunctionTypes to
>     // figure out A and T, then...
>     return bp::make_function(&set_helper<A,T,f>);
> }
>
>
> I'll leave it to you to fill in the details.  I haven't tried it myself,
> but I think it would work.
>
>
> Jim

Thanks for that hint, I'll certainly take a look. The only think that
would bother me with this solution is that I would need to disambiguate
between the non-const and const functions with the casting operator again.

Michael
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Jim Bosch-2
On 01/31/2012 12:41 AM, Michael Wild wrote:

> On 01/30/2012 07:28 PM, Jim Bosch wrote:
>> On 01/30/2012 12:11 PM, Michael Wild wrote:
>>> That's what I've been referring to as "auxiliary" functions. If
>>> possible, I'd like to avoid them because I don't fancy writing hundreds
>>> of those... I could live with calling a template. However, so far I
>>> haven't been able to come up with one that is sufficiently easy to use...
>>>
>>> So far I came up with the this:
>>>
>>> template<class ArgType, class T, T&(C::*method)()>
>>> void set_helper(T&   self, ArgType&   arg)
>>> {
>>>     (self.*method)() = arg;
>>> }
>>>
>>> But it is a bit cumbersome to use, as it doesn't deduce its first two
>>> template arguments on its own. Is there any way to achieve that feat?
>>>
>>
>> If you're up for playing with Boost.FunctionTypes, you should be able to
>> write a wrapper for make_function that deduces those types for you:
>>
>> namespace bp = boost::python;
>>
>> template<typename A, typename T, A&(T::*method)()>
>> void set_helper(T&  self, A const&  arg) {
>>      (self.*method)() = arg;
>> }
>>
>> template<class F>
>> bp::object make_set_helper(F f) {
>>      // F is the member function type; use Boost.FunctionTypes to
>>      // figure out A and T, then...
>>      return bp::make_function(&set_helper<A,T,f>);
>> }
>>
>>
>> I'll leave it to you to fill in the details.  I haven't tried it myself,
>> but I think it would work.
>>
>>
>> Jim
>
> Thanks for that hint, I'll certainly take a look. The only think that
> would bother me with this solution is that I would need to disambiguate
> between the non-const and const functions with the casting operator again.
>

You might be able to address that by having two make_helper functions,
using enable_if/disable_if to select one when the return type is const
and the other when it isn't.  The one invoked for const returns would
just call make_function directly to make a getter, while the non-const
one would be like make_set_helper above.


Jim
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Michael Wild
On 01/31/2012 05:56 PM, Jim Bosch wrote:

> On 01/31/2012 12:41 AM, Michael Wild wrote:
>> On 01/30/2012 07:28 PM, Jim Bosch wrote:
>>> On 01/30/2012 12:11 PM, Michael Wild wrote:
>>>> That's what I've been referring to as "auxiliary" functions. If
>>>> possible, I'd like to avoid them because I don't fancy writing hundreds
>>>> of those... I could live with calling a template. However, so far I
>>>> haven't been able to come up with one that is sufficiently easy to
>>>> use...
>>>>
>>>> So far I came up with the this:
>>>>
>>>> template<class ArgType, class T, T&(C::*method)()>
>>>> void set_helper(T&   self, ArgType&   arg)
>>>> {
>>>>     (self.*method)() = arg;
>>>> }
>>>>
>>>> But it is a bit cumbersome to use, as it doesn't deduce its first two
>>>> template arguments on its own. Is there any way to achieve that feat?
>>>>
>>>
>>> If you're up for playing with Boost.FunctionTypes, you should be able to
>>> write a wrapper for make_function that deduces those types for you:
>>>
>>> namespace bp = boost::python;
>>>
>>> template<typename A, typename T, A&(T::*method)()>
>>> void set_helper(T&  self, A const&  arg) {
>>>      (self.*method)() = arg;
>>> }
>>>
>>> template<class F>
>>> bp::object make_set_helper(F f) {
>>>      // F is the member function type; use Boost.FunctionTypes to
>>>      // figure out A and T, then...
>>>      return bp::make_function(&set_helper<A,T,f>);
>>> }
>>>
>>>
>>> I'll leave it to you to fill in the details.  I haven't tried it myself,
>>> but I think it would work.
>>>
>>>
>>> Jim
>>
>> Thanks for that hint, I'll certainly take a look. The only think that
>> would bother me with this solution is that I would need to disambiguate
>> between the non-const and const functions with the casting operator
>> again.
>>
>
> You might be able to address that by having two make_helper functions,
> using enable_if/disable_if to select one when the return type is const
> and the other when it isn't.  The one invoked for const returns would
> just call make_function directly to make a getter, while the non-const
> one would be like make_set_helper above.
>
>
> Jim

Sorry, I think you'll have to expand a bit on that. Do you mean that I
can use enable_if/disable_if to select between the two implementations?

Michael
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig
Reply | Threaded
Open this post in threaded view
|

Re: Boost.Python property definition question

Jim Bosch-2
On 01/31/2012 02:52 PM, Michael Wild wrote:

>> You might be able to address that by having two make_helper functions,
>> using enable_if/disable_if to select one when the return type is const
>> and the other when it isn't.  The one invoked for const returns would
>> just call make_function directly to make a getter, while the non-const
>> one would be like make_set_helper above.
>>
>>
>> Jim
>
> Sorry, I think you'll have to expand a bit on that. Do you mean that I
> can use enable_if/disable_if to select between the two implementations?
>

I think so; again, I haven't tested.

Here's the idea:

template <typename F>
typename boost::enable_if<
     boost::is_const<
         typename boost::function_types::result_type<F>::type
     >,
     bp::object
 >::type
make_helper(F f) {
     // deduce A and T ...
     return bp::make_function(
         (A const & (T::*() const))f,
         bp::return_value_policy<copy_const_reference>()
     );
}

template <typename F>
typename boost::disable_if<
     boost::is_const<
         typename boost::function_types::result_type<F>::type
     >,
     bp::object
 >::type
make_helper(F f) {
     // deduce A and T ...
     return bp::make_function(&set_helper<A,T,f>);
}

Hope that works!

Jim
_______________________________________________
Cplusplus-sig mailing list
[hidden email]
http://mail.python.org/mailman/listinfo/cplusplus-sig