[C++-sig] How about class_<...>.enable_copy() ?

Hans Meine meine at informatik.uni-hamburg.de
Mon Jul 23 19:22:15 CEST 2007


I still have the same problem as last time, that I want to be able to 
copy.copy(foo) and copy.deepcopy(foo) my BPL-exported objects:

Am Freitag, 08. Juni 2007 17:06:36 schrieb Hans Meine:
> This works if pickling support has been explicitly coded, by pickling and
> unpickling.  It would be neat if there was an .enable_copy() or sth. like
> that, which would define a __copy__ (or __deepcopy__) method that simply
> calls the copy constructor.  That would be more efficient than the pickling
> approach, both at runtime and in terms of saved LOCs.  Have you thought
> about such a thing?  Is there a common short idiom that does that already? 
> Is there a rationale for not providing copying support?

Ralf answered (thanks BTW) that I should export the copy constructor.
I think that is not a bad idea, but I would ideally like to have:

foo = Foo(other) # copy constructor as in C++ (1)
foo = copy.copy(other) # pythonic copying (2)
foo = copy.deepcopy(other) # deepcopying (3)

(1) is easy to do by exporting the copy constructor

(2) could be implemented by setting __copy__ to the above copy constructor, 
but one would assume __dict__ to be (non-deep)copied, too.

(3) definitely needs its own implementation, which needs to call deepcopy 

I would still believe that an .enable_copy() would be very handy.  It could 
possibly be controlled by an enum arg which of the above methods should be 

I tried to implement (2) and (3) manually today, but failed.  Mostly because I 
am not 100% sure how to mix Python C API and BPL code correctly.  I have the 
following general questions:

A) How do I convert a Foo * into a boost::python::object if I exported the Foo 
class?  Back in those days when I first used BPLv2, I wrote the 
following "nifty" function:

template<class T>
inline PyObject * managingPyObject(T *p)
    return typename manage_new_object::apply<T *>::type()(p);

Is there a better way?  Also, this only gives me a PyObject *, which brings me 
to my next question:

B) How do I convert PyObject * into a boost::python::object if it's a new 
reference?  Is this boost::python::detail::new_reference?  Why is this 
in "detail" then?

C) How do I convert PyObject * into a boost::python::object if it's a borrowed 
reference?  I guess that handle(borrowed(...)) helps here?

Finally, I wonder whether nobody else tried to implement __deepcopy__ with BPL 
yet - I could not find anything useful, searching the WWW for >2 hours now.

I tried to get the copy.deepcopy function which I need to call recursively 
like this:

    PyObject* pyDeepcopy =
        PyRun_String("import copy\ncopy.deepcopy",
                     Py_file_input, Py_None, Py_None);
        bp::call<bp::dict>(pyDeepcopy, map.attr("__dict__"), memo));

Unfortunatly, with empty globals and locals, __import__ is not found. :-(
Probably I should use PyImport_ImportModule or the new boost::python::import.
That seems to work, but the recursive call then fails:

  File "/software/python-2.4.4/lib/python2.4/copy.py", line 185, in deepcopy
    y = copier(x, memo)
TypeError: No to_python (by-value) converter found for C++ type: 

The code (which relies on 1.34.0) now looks like this (see attachment):

bp::object Foo__deepcopy__(bp::object foo, bp::dict memo)
    bp::object copyMod = bp::import("copy");
    bp::object deepcopy = copyMod.attr("deepcopy");

    Foo *newFoo(new Foo(bp::extract<const Foo &>(foo)));
    bp::object result(bp::detail::new_reference(managingPyObject(newFoo)));

        deepcopy(foo.attr("__dict__"), memo));

    return result;

Ciao, /  /
    /  / ANS
-------------- next part --------------
A non-text attachment was scrubbed...
Name: foo_copying.cxx
Type: text/x-c++src
Size: 838 bytes
Desc: not available
URL: <http://mail.python.org/pipermail/cplusplus-sig/attachments/20070723/66f3eafa/attachment.cxx>

More information about the Cplusplus-sig mailing list