I'm having a problem with some python objects derived in python. I have a Clone method that's supposed to make a full copy of these python inherited objects, but it isn't working. The problem is my wrapper class' PyObject* self isn't getting copied, so when I use call_method<>(self, "method name"), self will never point to the copied instance. Here's a brief example showing the issue
C++ file: #include <boost/python.hpp>#include <scripting/Python/ScriptHelpers.h>using namespace boost::python; class BaseClass{public: typedef boost::shared_ptr<BaseClass> ClonePtr; BaseClass() : ID(++IDCounter) { } BaseClass(const BaseClass& cp) : ID(++IDCounter) { } virtual boost::shared_ptr<BaseClass> Clone() { return boost::shared_ptr<BaseClass>(new BaseClass(*this)); } virtual int GetID() const { return ID; } virtual void Test() { std::cout << "C++ side ID: " << ID << "\n"; }protected: int ID;private: static int IDCounter;}; class BaseClassWrap : public BaseClass{public: BaseClassWrap(PyObject* self_) : self(self_) { } BaseClassWrap(PyObject* self_, const BaseClass& cp) : self(self_), BaseClass(cp) { } BaseClassWrap(PyObject* self_, const BaseClassWrap& cp) : self(self_), BaseClass(cp) { } virtual int GetID() const override { return call_method<int>(self, "GetID"); } virtual void Test() { call_method<void>(self, "Test"); } void TestDefault() { BaseClass::Test(); } int GetIDDefault() const { return BaseClass::GetID(); } boost::shared_ptr<BaseClass> Clone() override { return call_method<ClonePtr>(self, "Clone"); } boost::shared_ptr<BaseClass> CloneDefault() const { return boost::shared_ptr<BaseClass>(new BaseClassWrap(*this)); } PyObject* self;private:}; BOOST_PYTHON_MODULE(TestModule){ class_<BaseClass, boost::shared_ptr<BaseClassWrap> >("BaseClass", init<>()) .def(init<const BaseClass&>()) .def(init<const BaseClassWrap&>()) .def("GetID", &BaseClassWrap::GetIDDefault) .def("Clone", &BaseClassWrap::CloneDefault) .def("Test", &BaseClassWrap::TestDefault) .def("__copy__", &generic__copy__< BaseClassWrap >) .def("__deepcopy__", &generic__deepcopy__< BaseClassWrap >) ;} int BaseClass::IDCounter = 0; int main(){ PyImport_AppendInittab("TestModule", initTestModule); Py_Initialize(); try { auto EngineModule = object( (handle<>(PyImport_ImportModule("TestModule"))) ); auto MainModule = object((handle<>(borrowed(PyImport_AddModule("__main__"))))); auto MainNamespace = MainModule.attr("__dict__"); MainNamespace["TestModule"] = EngineModule; object result = exec_file("D:\\FinalReport\\Test.py", MainNamespace, MainNamespace); object test = MainNamespace["test"]; boost::shared_ptr<BaseClass> basec = extract<boost::shared_ptr<BaseClass> >(test()); if (basec.get() != nullptr) { auto cl = basec->Clone(); basec->Test(); cl->Test(); std::cout << "Original ID " << basec->GetID() << "\n"; std::cout << "Clone ID " << cl->GetID() << "\n"; } } catch (boost::python::error_already_set) { PyErr_Print(); } return 0;} generic__copy__ and generic__deep__copy are in ScriptHelpers and shown below. They were pulled from an old thread on this mailing list and look like: #define PYTHON_ERROR(TYPE, REASON) \ { \ PyErr_SetString(TYPE, REASON); \ throw error_already_set(); \ } template<class T> inline PyObject * managingPyObject(T *p) { return typename manage_new_object::apply<T *>::type()(p); } template<class Copyable> object generic__copy__(object copyable) { Copyable *newCopyable(new Copyable(extract<const Copyable &>(copyable))); object result(boost::python::detail::new_reference(managingPyObject(newCopyable))); extract<dict>(result.attr("__dict__"))().update( copyable.attr("__dict__")); return result; } template<class Copyable> object generic__deepcopy__(object copyable, dict memo) { object copyMod = import("copy"); object deepcopy = copyMod.attr("deepcopy"); Copyable *newCopyable(new Copyable(extract<const Copyable &>(copyable))); object result(boost::python::detail::new_reference(managingPyObject(newCopyable))); // HACK: copyableId shall be the same as the result of id(copyable) //in Python - // please tell me that there is a better way! (and which ;-p) int copyableId = (int)(copyable.ptr()); memo[copyableId] = result; extract<dict>(result.attr("__dict__"))().update( deepcopy(extract<dict>(copyable.attr("__dict__"))(), memo)); return result; } and the python file: from TestModule import *import copy class Der(BaseClass): def Test(self): print "Python Side Test. ID: " + str(self.GetID()) def Clone(self): print "Python Clone" return copy.deepcopy(self) def test(): return Der() Sorry about the length, just want to make sure I got everything relevant in. When I run this code, it prints: Python Clone Python Side Test. ID: 1Python Side Test. ID: 1Original ID 1Clone ID 1 this is wrong because Clone ID should be 2 (inspecting the object confirms it is 2). Like I said I'm pretty sure the problem is that the wrapper class' copy constructor only makes a copy of the PyObject* self, not a full copy. This is how every reference doc I saw was doing class wrapping, but isn't this wrong? Should we be making a full copy of the PyObject*? How would I go about doing that, or otherwise modifying my classes to get the behaviour I expect. Thanks
_______________________________________________ Cplusplus-sig mailing list Cplusplus-sig@python.org http://mail.python.org/mailman/listinfo/cplusplus-sig