Is it possible to pass a boost shared_ptr from C++ to Python and back to C++
Asked Answered
I

2

9

I have an object that is held in a shared_ptr in C++. The object is accessed through the python bindings from within python and passed to another bound C++ function that tries to keep hold of it. It appears that when the object goes from C++ to Python it is converted from a shared_ptr into a python object. Then when it goes back into C++ it is converted from the python object into a new shared_ptr, however this shared_ptr is unrelated to the initial holding shared_ptr.

Is it possible to set up the boost-python bindings so that the conversion from python object to shared_ptr references the original shared_ptr internals?

Below is the abridged code I have used to show the problem.

In this example an object is initially held in a shared_ptr named s_inital. It is grabbed from within python through the getSharedPtr function, then pushed back into C++ through the putSharedPtr function. Inside putSharedPtr it is copied into the weak_ptr s_copied. Inspecting the pointers in the debugger shows that the shared_ptr used in putSharedPtr does not have the same reference counting internals as s_initial. The final assert will fire because the weak pointer s_copied was only related to a single strong pointer (the pointer used in putSharedPtr) and that pointer was destructed once putSharedPtr was finished.

static shared_ptr<Captured> s_initial;
static weak_ptr<Captured> s_copied;

class UseSharedPtr
{
public:
    shared_ptr<Captured> getSharedPtr()
    {
        return s_initial;
    }

    void putSharedPtr(shared_ptr<Captured> ptr)
    {
        s_copied = ptr;
    }
};


BOOST_PYTHON_MODULE(test)
{
    class_<Captured, shared_ptr<Captured>, boost::noncopyable>("Captured", no_init);
    class_<UseSharedPtr, boost::noncopyable>("UseSharedPtr", init<>())
        .def("getSharedPtr", &UseSharedPtr::getSharedPtr)
        .def("putSharedPtr", &UseSharedPtr::putSharedPtr)
    ;
}



    s_initial = make_shared<Captured>();

    const char* chunk = "\
from test import UseSharedPtr \n\
x = UseSharedPtr() \n\
ptr = x.getSharedPtr() \n\
x.putSharedPtr(ptr)\n\
del x \n\
del ptr \n\
";
    object result = exec(chunk, mainNamespace, mainNamespace);

    assert(s_copied.lock());
Imitative answered 31/8, 2012 at 1:11 Comment(2)
What is the purpose of the whole UseSharedPtr class? Just as Captured is exposed now, boost::python will handle that correctly.Tenderize
I think this was just meant as an example. While boost.python will correctly call methods of the shared class, it has problems passing the shared_ptr as parameter to other classes.Merell
T
0

You need class_<UseSharedPtr, shared_ptr<UseSharedPtr>, boost::noncopyable> so that boost::python knows it should manage the object using shared_ptr. Make your example compilable if you want me to verify that.

Tenderize answered 28/11, 2012 at 12:43 Comment(1)
The shared_ptr and noncopyable flag are already present in the posted example.Merell
M
0

One easy but ugly workaround would be to use enable_shared_from_this() for the Captured class and get a fresh shared_ptr for each parameter using shared_from_this().

void putSharedPtr(shared_ptr<Captured> ptr)
{
    s_copied = ptr->shared_from_this();
}
Merell answered 22/3, 2017 at 14:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.