patternpythonMinor
Same PyObject for a shared_ptr C++ object
Viewed 0 times
pyobjectsameshared_ptrforobject
Problem
I have a similar problem as in this mail. I want that every time I return the same C++ managed object to Python, I get the same
I wrote a template converter which takes as input a
In this case, I added a function to manually remove an entry. This works because the object is managed in C++ and I can intercept than the object should be removed from the C++ side. If Python has still a reference to this object, it's ok to wait for the GC to collect the Python side and implicitly remove the C++ object. If the last reference is removed in Python, the
I made a couple of test, which shows the validity, that can be found here.
The usage is:
Is the following code enough to fulfill the requirements for any C++ class wrapped in a
```
#pragma once
#include
#include
#include
template
using storage_map = std::map;
template
storage_map* getCachedObjects() {
static storage_map *storage = new storage_map();
return storage;
}
template
using c_sptr = boost::shared_pt
PyObject. With the standard converter I get always a new wrapper-PyObject. The solution should fulfill the following requirements:get() == get()(the Python object is always equal, whatever function returns the same C++ object)
get()isget()
- The same is valid for any objects returned in containers (which the solution from the mail lacks)
getArray()[0] == get()
- I can instance attributes (
get().new_var = "xx"->get().new_var == "xx")
I wrote a template converter which takes as input a
shared_ptr. It looks in a static map, if this shared_ptr is already returned, than it returns the existing PyObject, else a new one is created. I haven't found a solution there I can automatically remove unused objects.In this case, I added a function to manually remove an entry. This works because the object is managed in C++ and I can intercept than the object should be removed from the C++ side. If Python has still a reference to this object, it's ok to wait for the GC to collect the Python side and implicitly remove the C++ object. If the last reference is removed in Python, the
PyObject is still valid, because the map holds still a reference. Also , as long as the PyObject inside the map exists, the C++ object cannot be removed from the heap.I made a couple of test, which shows the validity, that can be found here.
The usage is:
boost::python::to_python_converter,
c_ptr_to_cached_python>();Is the following code enough to fulfill the requirements for any C++ class wrapped in a
shared_ptr? Is the PyObject generation valid and can it be improved?```
#pragma once
#include
#include
#include
template
using storage_map = std::map;
template
storage_map* getCachedObjects() {
static storage_map *storage = new storage_map();
return storage;
}
template
using c_sptr = boost::shared_pt
Solution
I'm not sure I understand all the premises of this question. My kneejerk reaction was, "Would it help if you could assume that
Anyway, I see a bit of reducible complexity in your code, I think.
Isn't this just a really complicated way of saying
(since
which ought to be more like
Even that looks more complicated than it feels like it ought to be, but I'm not finding much documentation about what
I see you use
Are you at all worried that
C_CLASS derived from std::enable_shared_from_this? Is it in your power to make that an invariant in your codebase?" If so, I would even go a step further and ask whether you should just write an enable_PyObject_from_this along the same lines.Anyway, I see a bit of reducible complexity in your code, I think.
template
using py_ptr = typename boost::python::pointee>::type;Isn't this just a really complicated way of saying
template using py_ptr = T;(since
pointee>::type is ptr::element_type is T by definition)? And then you use it in contexts liketemplate
PyObject * createObject(const c_sptr& obj) {
return boost::python::objects::make_ptr_instance,
boost::python::objects::pointer_holder,
py_ptr>>::execute(obj);
}which ought to be more like
template
PyObject *createObject(const std::shared_ptr& obj) {
using namespace boost::python::objects;
return make_ptr_instance, T>>::execute(obj);
}Even that looks more complicated than it feels like it ought to be, but I'm not finding much documentation about what
make_ptr_instance is supposed to do (or for that matter why the second template type parameter to pointer_holder isn't defaulted).I see you use
Py_INCREF (all caps) but Py_DecRef (camel case). This suggests to the reader that something really subtle (and thus dangerous) is happening here — or else of course you'd be consistently using Py_IncRef/DecRef or Py_INCREF/DECREF (both of which are provided by the underlying library). But is the reader's inference correct? Are you really doing something subtle and dangerous? Or are you just mixing styles because you felt like it?Are you at all worried that
getCachedObjects() returns a completely different map from getCachedObjects(), even though I might have a std::shared_ptr b and a std::shared_ptr d such that b == d?Code Snippets
template<typename C_CLASS>
using py_ptr = typename boost::python::pointee<boost::shared_ptr<C_CLASS>>::type;template<typename T> using py_ptr = T;template<typename C_CLASS>
PyObject * createObject(const c_sptr<C_CLASS>& obj) {
return boost::python::objects::make_ptr_instance<py_ptr<C_CLASS>,
boost::python::objects::pointer_holder<c_sptr<C_CLASS>,
py_ptr<C_CLASS>>>::execute(obj);
}template<typename T>
PyObject *createObject(const std::shared_ptr<T>& obj) {
using namespace boost::python::objects;
return make_ptr_instance<T, pointer_holder<shared_ptr<T>, T>>::execute(obj);
}Context
StackExchange Code Review Q#130044, answer score: 2
Revisions (0)
No revisions yet.