NodePath lifetime (Python/C++)

Hello,
this is another question about writing C++ extensions for Panda3D.

What I do: In Python I load a NodePath, but only in local namespace. So the Python object will dealloc as soon as the current method finishes. Then I create an object from my extension and pass the NodePath to this object. The extension objects unpacks the C++ NodePath pointer and stores the NodePath* as a member.

My problem: The extension objects will be alive longer than the (Python) NodePath objects. I want to use the C++ NodePath pointer even after the Python NodePath was destroyed, e.g. like in this pseudo code:

import extension

    ....

    def setup( self ):
        np = loader.loadModel( ... )
        self.foo = extension.Foo( )
        self.foo.setNodePath( np )
        # np will dealloc after this line

    def test( self ):
        print self.foo.getNodePath( )

NodePath doesn’t inherit from ReferenceCount, so I can’t just create another reference in C++ like I could with e.g. geomNode: “geom_node->ref( )”.

So far I have come up with two possible solutions:

(1)
The extension object stores not only the NodePath*, but the Python NodePath objects too (and use Py_INCREF/DECREF when setting the member, initialize to Py_Node and so on…). This way the Python object will be alive as long as the extension object:

typedef struct {
    PyObject_HEAD

    NodePath*            m_np;
    PyObject*            m_npObject;
} PyFooObject;

(2)
The extension objects doesn’t store the C++ NodePath*, but creates a copy of this NodePath using the copy constructor (and free the allocated object again in ~Foo or when setting again):

    NodePath* np = (NodePath*) ( (Dtool_PyInstDef*) _np )->_ptr_to_object; // unpack
    self->m_np = new NodePath( *np ); // copy constructor

Both solutions seem to work, but I want to ask if somebody knows possible issues with on or the other way, or perhaps knows a third way.

Thanks,
enn0x

Option (2) is (almost) your best choice. NodePath is designed to be a lightweight object, easily and cheaply copied. But I would just store a NodePath object directly, rather than allocating and freeing a pointer to a NodePath.

In general, there are two kinds of C++ classes in Panda. Those that are intended to be referenced by pointer, and those that are intended to be used as concrete objects. Usually, the pointer-based classes inherit from ReferenceCount, and the concrete objects don’t.

NodePath is intended to be a concrete object.

David

Thank you for your advice, David.

But now I have a question. When I only unpack and store the NodePath* then I get crashes once I access the pointer again. I can avoid the crashes by storing the Python NodePath object either in C++ or Python (e.g. “self.np = np” in the above pseudo code).

So my conclusion has been that once the Python NodePath object deallocates the underlying C++ NodePath gets destroyed too and my stored pointer gets invalid. I didn’t check this in the Panda3D source though. Is this assumption wrong?

enn0x

That’s right: once the Python NodePath object gets destroyed, it takes the C++ NodePath with it.

That’s why I recommended storing a concrete NodePath, not a pointer to a NodePath. Like this:

typedef struct {
    PyObject_HEAD

    NodePath            m_np;
} PyFooObject;

    NodePath* np = (NodePath*) ( (Dtool_PyInstDef*) _np )->_ptr_to_object; // unpack
    self->m_np = *np;

See? It’s basically option (2) in your list, but there’s no need to mess with new or delete: NodePath is a concrete object, like an int.

David

Ok, got you now. Works like a charm. Thank you again for the help.
enn0x