[C++-sig] Properly copying wrapped classes

Jay Riley super24bitsound at hotmail.com
Sun Jun 10 00:50:46 CEST 2012


That'll work in the simplified example I had. But it's going to be a huge pain for many of my actual classes.
I guess I'll have to do it like this if there's no other options? Though at that point I might need to limit how much functionality I'm exporting out to python.
> Date: Fri, 8 Jun 2012 21:22:47 -0400
> From: talljimbo at gmail.com
> To: super24bitsound at hotmail.com
> CC: cplusplus-sig at python.org
> Subject: Re: [C++-sig] Properly copying wrapped classes
> 
> It looks like the only way to get a wrapper object is by actually 
> invoking the Python constructor.  That's not what I expected, but it's 
> reasonable and not completely unexpected.  And it avoids problems that 
> might arise if you had any Python-only attributes attached to the 
> wrapper object.
> 
> So I think you'll need to try a new approach, one that involves calling 
> a wrapped copy constructor.
> 
> Would it meet your needs to just remove the __copy__ and __deepcopy__ 
> implementations, and change the Python derived class to something like
> what's below?
> 
> class Der(BaseClass):
>      def Test(self):
>          print "Python Side Test. ID: " + str(self.GetID())
> 
> def Clone(self):
>      print "Python Clone"
>      result = Der(...)    # whatever constructor args you need in Python
>      BaseClass.__init__(result, self)
>      return result
> 
> 
> (btw, apologies to the rest of the list - I accidentally replied without 
> including the list a few emails back, but all you've missed are a number 
> of ideas that don't work)
> 
> 
> Jim
> 
> 
> 
> 
> 
> On 06/08/2012 08:40 PM, Jay Riley wrote:
> > I replaced my template copy with
> >
> > object bc_copy(object copyable, dict memo)
> > {
> > object copyMod = import("copy");
> > object deepcopy = copyMod.attr("deepcopy");
> >
> > BaseClassWrap *newCopyable(new BaseClassWrap(extract<const BaseClassWrap
> > &>(copyable)));
> >
> > BaseClass tmp = boost::python::extract<BaseClass const &>(copyable);
> > object result(tmp);
> >
> > int copyableId = (int)(copyable.ptr());
> > memo[copyableId] = result;
> >
> > extract<dict>(result.attr("__dict__"))().update(
> > deepcopy(extract<dict>(copyable.attr("__dict__"))(),
> > memo));
> >
> > return result;
> > }
> >
> > and wrapped
> >
> > 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__< BaseClass >)
> > .def("__deepcopy__", &bc_copy)
> > ;
> >
> > But I'm still getting the non overriden behaviour. Calling Test on the
> > clone prints out the C++ side code, while the non clone prints out the
> > python side code.
> >
> >
> >
> >
> >
> >  > Date: Thu, 7 Jun 2012 17:13:55 -0400
> >  > From: jbosch at astro.princeton.edu
> >  > To: super24bitsound at hotmail.com
> >  > Subject: Re: [C++-sig] Properly copying wrapped classes
> >  >
> >  > On 06/05/2012 12:41 AM, Jay Riley wrote:
> >  > > That got rid of the compile time errors. However, On the python side I
> >  > > get the following error when I try to clone:
> >  > >
> >  > >
> >  > > File ... line 9 in Clone
> >  > > return copy.deepcopy(self)
> >  > > File C:\Python27\Lib\copy.py" line 174 in deepcopy
> >  > > y = copier(memo)
> >  > > TypeError: No to_python (by-value) converter for C++ type: class
> >  > > BaseClassWrap
> >  > >
> >  >
> >  > Hmm. I would have expected that to work. As long as BaseClass can be
> >  > instantiated itself (i.e. it isn't pure abstract), you could try this in
> >  > the copy/deepcopy implementation:
> >  >
> >  > BaseClass tmp = boost::python::extract<BaseClass const &>(copyable);
> >  > object result(tmp);
> >  >
> >  > (I don't remember which of these types your Copyable template parameter
> >  > was, but I'd recommend just trying to write this in a non-template
> >  > function until you get it working).
> >  >
> >  > That will involve one extra copy, but it should invoke the BaseClass
> >  > by-value converter instead of the BaseClassWrap by-value converter, and
> >  > I *think* that should do the right thing.
> >  >
> >  >
> >  > Jim
> >  >
> >  >
> >  >
> >  >
> >  > > In the interest of trying it out, I added the BaseClassWrap to the
> >  > > python module
> >  > >
> >  > > class_<BaseClassWrap, bases<BaseClass> >("BaseClassWrap",
> > init<PyObject*>())
> >  > > .def(init<PyObject*, const BaseClass&>())
> >  > > .def(init<PyObject*, const BaseClassWrap&>())
> >  > > .def("__copy__", &generic__copy__< BaseClassWrap >)
> >  > > .def("__deepcopy__", &generic__deepcopy__< BaseClassWrap >)
> >  > > ;
> >  > >
> >  > > But this put me back at square one, where self isn't indicative of the
> >  > > cloned instance.
> >  > >
> >  > > Just in the interest of seeing the effect, I changed my python
> > exposing to:
> >  > >
> >  > > 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__< BaseClass >)
> >  > > .def("__deepcopy__", &generic__deepcopy__< BaseClass >)
> >  > > ;
> >  > >
> >  > > But as expected, the overloading doesn't work on clones made with this
> >  > > exposing. Anything else I should try?
> >  > >
> >  > > Again, thanks for the help so far, appreciate it.
> >  > >
> >  > >
> >  > >
> >  > >
> >  > > > Date: Mon, 4 Jun 2012 11:52:42 -0400
> >  > > > From: jbosch at astro.princeton.edu
> >  > > > To: super24bitsound at hotmail.com
> >  > > > Subject: Re: [C++-sig] Properly copying wrapped classes
> >  > > >
> >  > > > On 06/04/2012 12:58 AM, Jay Riley wrote:
> >  > > > >
> >  > > > > Hi Jim,
> >  > > > > Thanks for the help so far. From what you said, I should be able to
> >  > > change my deepcopy to this correct?
> >  > > > >
> >  > > > > 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)));
> >  > > > > object result(extract<const Copyable&>(copyable)());
> >  > > > > // 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; }
> >  > > > > When I attempt to compile it gives me the following error:
> >  > > > > Error 48 error C2664: 'boost::python::api::object::object(const
> >  > > boost::python::api::object&)' : cannot convert parameter 1 from
> >  > > 'boost::python::api::object (__cdecl *)(boost::python::extract<T>
> >  > > (__cdecl *)(void))' to 'const boost::python::api::object&'
> >  > > c:\khmp\src\engine\scripting\python\scripthelpers.h 67
> >  > > > > Did you mean something else? This level of boost python is above my
> >  > > head, so I'm a bit lost
> >  > > > >
> >  > > >
> >  > > > It looks like the version I gave you is a bad corner case where the
> >  > > > compiler is incapable of parsing a constructor properly when
> > templates
> >  > > > and overloaded function-call operators are involved; I always forget
> >  > > > exactly when that comes into play (not a compiler bug, just a
> > weird part
> >  > > > of the standard, I think). In any case, I think separating it
> > into two
> >  > > > lines should fix the problem:
> >  > > >
> >  > > > const Copyable & tmp = extract<const Copyable &>(copyable);
> >  > > > object result(tmp);
> >  > > >
> >  > > > Let me know if that doesn't work either; at that point I should
> > try to
> >  > > > compile it myself and see what's going on.
> >  > > >
> >  > > > Jim
> >  > > >
> >  > > >
> >  > > >
> >  > > >
> >  > > >
> >  > > > >> Date: Sun, 3 Jun 2012 11:25:38 -0400
> >  > > > >> From: jbosch at astro.princeton.edu
> >  > > > >> To: cplusplus-sig at python.org
> >  > > > >> CC: super24bitsound at hotmail.com
> >  > > > >> Subject: Re: [C++-sig] Properly copying wrapped classes
> >  > > > >>
> >  > > > >> On 05/31/2012 08:49 PM, Jay Riley wrote:
> >  > > > >>> 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
> >  > > > >>>
> >  > > > >>
> >  > > > >> <snip>
> >  > > > >>
> >  > > > >>>
> >  > > > >>> 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: 1
> >  > > > >>> Python Side Test. ID: 1
> >  > > > >>> Original ID 1
> >  > > > >>> Clone 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.
> >  > > > >>>
> >  > > > >>
> >  > > > >> There are a couple of things here that probably need to be
> > addressed:
> >  > > > >>
> >  > > > >> - Be wary of relying on copy-constructor side effects to
> > demonstrate
> >  > > > >> whether something is working; the compiler is allowed to elide
> > copy
> >  > > > >> constructors under many conditions, which might result in
> > misleading
> >  > > > >> results. That said, I don't think that's what's causing your
> > problem
> >  > > > >> here (or at least not all of it).
> >  > > > >>
> >  > > > >> - Your implementations of __copy__ and __deepcopy__ won't work
> > with
> >  > > > >> polymorphic wrapper classes, because they construct a BaseClass
> >  > > instance
> >  > > > >> and tell Boost.Python to wrap it by managing the new
> > reference. That
> >  > > > >> necessarily makes a Python object that holds a BaseClass object,
> >  > > not one
> >  > > > >> that holds a BaseClassWrap object. If you want to get a
> > BaseClassWrap
> >  > > > >> Python object, you want to pass it by value to Boost.Python; that
> >  > > should
> >  > > > >> invoke the BaseClassWrap copy constructor and pass it a new
> >  > > PyObject* to
> >  > > > >> manage (so it doesn't have to copy the PyObject* itself).
> >  > > > >>
> >  > > > >> I think that just means you can remove the "managingPyObject"
> >  > > function,
> >  > > > >> and instead use:
> >  > > > >>
> >  > > > >> object result(extract<const Copyable&>(copyable)());
> >  > > > >>
> >  > > > >>
> >  > > > >> HTH
> >  > > > >>
> >  > > > >> Jim
> >  > > > >
> >  > > >
> >  >
> 
 		 	   		  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20120609/0f74c3c5/attachment-0001.html>


More information about the Cplusplus-sig mailing list