[C++-sig] Re : How to wrap this overloaded member function whose return type is reference ?
David Abrahams
dave at boost-consulting.com
Sat Feb 22 23:40:17 CET 2003
Nicodemus <nicodemus at globalite.com.br> writes:
> Hi David,
>
<snip>
> I will join the discussion, since I'm having a similar problem. 8)
>
> I'm trying to do that to export static members, but it does not work
> correctly.
>
> struct C
> {
> static double x;
> }; double C::x = 10;
>
> double get_x()
> {
> return C::x;
> }
>
> // Module
> ======================================================================
> BOOST_PYTHON_MODULE(boost)
> {
> object C_scope =
> class_< C >("C", init< const C & >())
> .def(init< >())
> ;
> C_scope.attr("x") = C::x;
> def("get_x", &get_x);
> }
>
>
> From python:
>
> >>> from boost import *
> >>> C.x
> 10.0
> >>> C.x = 3.4
> >>> C.x
> 3.9999999999
> >>> get_x()
> 10.0
> The x static member is not being updated in the C++ side, which
> seems obvious, since the call
> C_scope.attr("x") = C::x;
>
> is passing C::x by value. I tried this instead
>
> C_scope.attr("x") = &C::x;
>
> in the hopes that it would now update the variable correctly, but from
> python:
>
> >>> from boost import *
> Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> TypeError: No to_python (by-value) converter found for C++ type: double
>
> which is also obvious: I am declaring the attribute x as a double*,
> and boost.python doesn't know how to handle a double*.
>
> So, how can I get the correct behaviour?
Well, if x were a class you could do:
C_scope.attr("x") = boost::ref(C::x);
Which would wrap a reference to x. Unfortunately, you have another
problem: x is a double, and Python already has a type for
double... which is immutable.
The solution is to use a property to intercept attribute reads and
assignments, so that we can programmatically set C::x:
[ You should read http://www.python.org/2.2.2/descrintro.html and
http://www.python.org/peps/pep-0252.html if you haven't already.
Why they can't get this material into the regular Python docs is
beyond me
]
>>> #define a property class
... class Prop(object):
... def __get__(self, obj, type=None):
... print '__get__', (self, obj, type)
... return 'value'
...
... def __set__(self, obj, type=None):
... print '__set__', (self, obj, type)
...
... def __delete__(self, obj, type=None):
... print '__delete__', (self, obj, type)
...
>>> # use it in a class C
... class C(object):
... x = Prop()
...
>>> a = C()
>>> a.x # all accesses to a.x are intercepted
__get__ (<__main__.Prop object at 0x00877BC8>, <__main__.C object at 0x00878108>, <class '__main__.C'>)
'value'
>>> a.x = 42
__set__ (<__main__.Prop object at 0x00877BC8>, <__main__.C object at 0x00878108>, 42)
>>> C.x # Prop intercepts reads of the class attribute
__get__ (<__main__.Prop object at 0x00877BC8>, None, <class '__main__.C'>)
'value'
>>> C.x = 1 # But not assignments
>>> C.x
1
>>> class mc(object.__class__): # to intercept C.x assignment
... x = Prop() # I have to define this
...
>>> class C(object):
... __metaclass__ = mc
...
>>> C.x # now all accesses to C.x are intercepted
__get__ (<__main__.Prop object at 0x00876AB8>, <class '__main__.C'>, <class '__main__.mc'>)
'value'
>>> C.x = 1
__set__ (<__main__.Prop object at 0x00876AB8>, <class '__main__.C'>, 1)
>>> a = C() # But not accesses to a.x
>>> a.x
Traceback (most recent call last):
File "<stdin>", line 1, in ?
AttributeError: 'C' object has no attribute 'x'
>>>
As you can see, the only way to intercept assignment to C.x is to
stick a property C's class, i.e. the metaclass (or to modify
__setattr__ in the metaclass, but it amounts to the same thing). In
fact, to fully emulate the C++ syntaxes of:
C::x = 1;
and
a.x = 1;
We need a property in both the metaclass and in the class.
This leaves us with two options, both of which involve changing
Boost.Python's C++ source:
option 1:
introduce a new metaclass for every wrapped class so that we have a
place to stick a property object.
option 1a:
only do this if the user supplies a special extra template
parameter to the class_<...> declaration
option 2:
Implement a special property type which allows us to easily
identify property attributes which correspond to Boost.Python static
data members
Implement a special __setattr__ in the Boost.Python metaclass which
looks up the attribute on the class to see if it has this special
type; if so, it is called, and otherwise the default __setattr__
behavior takes effect.
I guess I prefer option 1: it favors speed and code size over data
size. The only downside I can see is that an extra object (the
metaclass object) is created for every wrapped class. It's also
simpler to implement.
Thoughts?
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
More information about the Cplusplus-sig
mailing list