Open andioz opened 4 years ago
OK, I was able to track down the problem towards a keep_alive
problem. Reading the manual more carefully I found this:
Keep alive
In general, this policy is required when the C++ object is any kind of container and another objects being added to the container. keep_alive<Nurse, Patient> indicates that the argument with index Patient should be kept alive at least until the argument with index Nurse is freed by the garbage collector.
Conclusion: I need to use keep_alive
for instances held in C++. But now I have 2 issues:
keep_alive
seems to work only for def
setter functions, not for def_readwrite
and def_property
? It compiles, but I don't see an effect.Is it possible to use keep_alive
with other definition types like def_readwrite
and def_property
? And is it possible to use the variable holding the reference as nurse instead of the whole container structure?
Here another, simplified example code:
#include <pybind11/pybind11.h>
#include <iostream>
#include <memory>
namespace py = pybind11;
class Interface {
public:
virtual ~Interface() = default;
virtual unsigned long value() const = 0;
};
struct Structure {
std::shared_ptr<Interface> instance;
};
class InterfaceTrampoline : public Interface {
public:
using Interface::Interface;
virtual unsigned long value() const override {
PYBIND11_OVERLOAD_PURE(unsigned long, Interface, value);
}
};
PYBIND11_MODULE(example, m) {
py::class_<Interface, std::shared_ptr<Interface>, InterfaceTrampoline>(m, "Interface")
.def(py::init<>())
.def("value", &Interface::value);
py::class_<Structure>(m, "Structure")
.def(
py::init<const std::shared_ptr<Interface>&>(),
py::keep_alive<1, 2>(),
py::arg("instance") = nullptr
)
.def_readwrite(
"instance",
&Structure::instance,
py::keep_alive<1, 2>()
)
.def_property(
"instance2",
[](Structure& structure){ return structure.instance; },
[](Structure& structure, const std::shared_ptr<Interface>& instance){ structure.instance = instance; },
py::keep_alive<1, 2>()
)
.def(
"instance3",
[](Structure& structure, const std::shared_ptr<Interface>& instance){ structure.instance = instance; },
py::keep_alive<1, 2>()
)
;
}
import sys
from example import *
class Derived(Interface):
def __init__(self):
Interface.__init__(self)
print("*** init ***", self)
return
def __del__(self):
print("*** del ***", self)
return
def value(self):
return 42
d = Derived()
print("d refcount (initial):", sys.getrefcount(d))
s = Structure(d)
print("d refcount (after s = Structure(d)):", sys.getrefcount(d))
s.instance = d
print("d refcount (after s.instance = d):", sys.getrefcount(d))
s.instance2 = d
print("d refcount (after s.instance2 = d):", sys.getrefcount(d))
s.instance3(d)
print("d refcount (after s.instance3(d)):", sys.getrefcount(d))
s.instance = None
print("d refcount (after s.instance = None):", sys.getrefcount(d))
del s
print("d refcount (after del s):", sys.getrefcount(d))
del d
print("finished")
I found the solution for def_property
here
https://gitter.im/pybind/Lobby?at=5da73ece2d59854e7f13faa8AC
I have to wrap the setter function with py::cpp_function() and append the py::keep_alive<1, 2>() part as second argument.
Only left the question using it in def_readwrite
and the second question about releasing the object on overwriting the attribute.
Hi,
First, thank you for this great library, it saves many weeks of work for me! I could solve most things in a simple and direct way, but with this construction I'm struggling. I found some hints in issues, stackoverflow, etc, but no direct answer to this problem, therefore let me give you a concrete example here.
I have a C++ interface
Image
, which is meant to be implemented on Python side. I want to pass an instance or this to C++, which works when it is passed directly as a parameter to a C++ function or method. But more complicated, I want to create more complex data structures, which contains references (shared pointer in this case) to such images. Now using this images passed into C++ function or method doesn't work, I guess themagic
for the transfer is not done because the image reference is passed indirectly.Example code: here I paste in the C++ and Python code, the complete files are attached here derrived-in-struct-problem.zip
Do I miss something, does a clean solution exist? Or do I need some kine of adapter for a workaround?
Thank you in advance! Andi
example.cpp: