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

Reply via email to