[C++-sig] set python attribute from C++
David Abrahams
dave at boostpro.com
Thu Jul 17 17:54:07 CEST 2008
on Thu Jul 17 2008, Gennadiy Rozental <rogeeff-AT-gmail.com> wrote:
> David Abrahams <dave <at> boostpro.com> writes:
>> > This one does work. But if you switch to intrusive_ptr it;s not
>> > anymore ;(. And this is exactly the scenario I am using. Why
>> > is that??
>>
>> shared_ptr is special. Custom deleters allow us to do some magical
>> things, by maintaining ownership of the Python object that wraps
>> the C++ object.
>
> Why is that by the way? It seems to me that intrusive_ptr is treated like
> distant relative to shared_ptr, while the only real difference is in storage
> policy for the counter.
It doesn't have a deleter.
>
>> Why do you want to use intrusive_ptr here?
>
> It's not even intrusive_ptr, but rather our own home grown smart
> pointer with properties similar to intrusive_ptr. This is forced on me
> by the framework I am working in.
I'm not entirely convinced that you need your own home grown smart
pointer in the particular code we're discussing. Any wrapped T is
convertible to shared_ptr<T>, no matter what method is used to "hold"
the T in the class_<T, ...> declaration.
>> > What's more my problem is a bit more complicated. What I ma after is to
>> > initialize object A in it's init method (ideally would be in
>> > constructor, but
>> > this is obviously impossible - derived class constructor in
>> > python is not even called yet).
>>
>> I don't understand what you're saying. All C++ class instances will be
>> constructed with a real call to their constructor.
>
> I mean I can't do what I am doing inside init function inside A
> constructor, because constructor of derived class in not called yet.
Hm.
>> > So the usage scenario is something like this:
>> >
>> > // this may create objects based on pure C++ classes
>> > // or classes implemented in python.
>> > boost::intrusive_ptr<Base> obj = Factory::create(....);
>>
>> This can't work for "classes implemented in Python." The intrusive_ptr
>> will not be able to maintain Python's reference count.
>
> This is really out of my hands. All our objects are memory managed
> through these intrusive_ptr like smart pointer. I guess what I need is
> to prevent Boost.Python from managing the C++ objects and manually
> sync lifetime of C++ anf python object.
>
>> It sounds like you're trying to invert the natural ownership
>> relationship. Python wrapper objects own their underlying C++
>> (sub)objects, not the other way around.
>
> Ok. Here is my latest attempt schematically:
>
> class Base {
> // this one is intrusively reference counted
> // any framework object is required to inherit from it
>
> ....
> };
>
> class_<Base,intrusive_ptr<Base>,noncopyable)( "Base" )
^^^^^^^^^^^^^^^^^^^
This automatically ensures that you can convert Python objects derived
from the Python Base into an intrusive_ptr<Base>. Converting in the
other direction works too, but it produces a new Python object.
> ....
> ;
>
> class PythonBase : public Base {
> public:
> ....
> void init( bp::object const& o ) { m_object = o; }
>
> void set_field( std::string const& field, intrusive_ptr<Base> value )
> {
> // here I am using global conversion registry I maintain
> m_object.attr( field ) = convert_to_python_object( value )
I don't fully understand, but does it matter?
> }
>
> private:
> bp::object m_object;
> };
>
> class_<PythonBase,PythonBase*,noncopyable>( "PythonBase" )
^^^^^^^^^^^
Very dangerous; could lead to leaks of C++ objects. However if you want
the C++ object to own a Python object, it seems like an option.
> ....
> ;
>
> Base*
> PythonFactory::create(...)
> {
> bp::object obj = m_py_type();
If m_py_type is the PythonBase Python class, there is nothing managing
the C++ PythonBase object's lifetime. The fact that PythonBase is
derived from Base and base is held by an intrusive_ptr makes no
difference. OTOH, If m_py_type is the "Base" python class, the
following extract should fail.
> PythonBase* ptr = bp::extract<PythonBase*>( obj );
>
> ptr->init( obj );
Now obj /and/ the unmanaged C++ PythonBase object are both managing
the lifetime of the python object
> return ptr;
So I guess if you capture the Base* with an intrusive_ptr here, you're
OK (so far). However, you haven'd dealt with the possibility of an
exception during create().
> }
> On python side I've can now derive from PythonBase:
>
> class Derived(PythonBase):
> pass
>
> Now i am seems to be able to create instances of Derived class and set their
> field from c++. Does this solution make sence?
I'm not 100% certain, but it looks like it will leak C++ PythonBase
instances. You might try putting some tracing statements in the ctors
and dtors to see if they are actually going away when they should.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
More information about the Cplusplus-sig
mailing list