Raw constructor (i.e. combination of make_constructor and raw_function)
Hi! I guess I am not the first one trying to do this, but the closest discussion I could find did not solve this topic: http://mail.python.org/pipermail/c++-sig/2005-June/008959.html I am trying to do something like python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1)); But this gives the vector as first element of the args tuple to createVector(). Now, my createVector looks like this: MyVector createVector(const python::tuple &args, const python::dict &) { int size = python::len(args); MyVector result(size); for(int i=0; i<size; ++i) result[i] = python::extract<double>(args[i])(); return result; } Is there a way to combine the functionality of raw_function and make_constructor? (I even tried staticmethod("__init__") ;-) ) -- Ciao, / / .o. /--/ ..o / / ANS ooo
Hans Meine <hans_meine@gmx.net> writes:
Hi!
I guess I am not the first one trying to do this, but the closest discussion I could find did not solve this topic: http://mail.python.org/pipermail/c++-sig/2005-June/008959.html
I am trying to do something like
python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1));
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that? -- Dave Abrahams Boost Consulting www.boost-consulting.com
Hi again, and thanks for your answer! On Monday 22 August 2005 02:47, David Abrahams wrote:
I am trying to do something like
python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1));
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that?
Err, I was always trying to wrap some kind of factory functions. You mean I should write initialization functions instead of constructing ones? Hmm, I tried that, but again have problems: My first try was to have an initialization function like this: void initVectorValue(const python::tuple &args, const python::dict &) { MyVector &result((python::extract<MyVector &>(args[0])())); int size = python::len(args) - 1; result.resize(size); for(int i=0; i<size; ++i) result[i] = python::extract<double>(args[i + 1])(); } and export that with .def("__init__", raw_function(&initVectorValue, 2)) However, that would not compile with either 1.31.0 or 1.32.0, I got errors like | /software/boost-1.32.0/include/boost-1_32/boost/python/raw_function.hpp: In | member function `PyObject* | boost::python::detail::raw_dispatcher<F>::operator()(PyObject*, PyObject*) | [with F = void (*)(const boost::python::tuple&, const boost::python::dict&)] | ': | /software/boost-1.32.0/include/boost-1_32/boost/python/object/py_function.hpp:92: instantiated from `PyObject* boost::python::objects::full_py_function_impl<Caller, Sig>::operator() (PyObject*, PyObject*) [with Caller = boost::python::detail::raw_dispatcher<void (*)(const boost::python::tuple&, const boost::python::dict&)>, Sig = boost::mpl::vector1<PyObject*>]' | /software/boost-1.32.0/include/boost-1_32/boost/mpl/if.hpp:67: instantiated from here | /software/boost-1.32.0/include/boost-1_32/boost/python/raw_function.hpp:29: error: invalid | use of void expression I tried changing the return type of my function to MyVector &, but then trying to construct PythonVector objects results in: | TypeError: No registered converter was able to extract a C++ reference to | type vigra::PythonVector<float> from this Python object of type PythonVector BTW: vigra::PythonVector<float> is MyVector above, which I exported to python simply with python::class_<MyVector>("PythonVector", python::no_init).def(... I am currently compiling 1.33.0 in order not to blame myself in that respect. Ideally, I'd like to wrap factory functions like the createVector() I originally posted. Then, one would not need default constructible classes whose default constructed objects can be brought to the correct state. (E.g. MyVector should be of fixed, but variable size that can be set in the constructor.) For now, I would be happy with a C++ resize(), but I need a workaround for the raw_function constructor. The real motivation is that I want to pickle my vector objects, and AFAICS I need the constructor for this. So far, I just exported the beforementioned createVector() factor function as a module function to python and used that for creating my objects, but how should pickle know that? Ciao, / / /--/ / / ANS
On Monday 22 August 2005 14:20, Hans Meine wrote:
I am currently compiling 1.33.0 in order not to blame myself in that respect.
OK, just FYI, I tried your proposed initVectorValue() approach with 1.33.0, too - both with void and MyVector& as return types, with the same results. I am looking forward to further pointers. :-/
For now, I would be happy with a C++ resize(), but I need a workaround for the raw_function constructor. The real motivation is that I want to pickle my vector objects, and AFAICS I need the constructor for this. So far, I just exported the beforementioned createVector() factor function as a module function to python and used that for creating my objects, but how should pickle know that?
Ciao, / / /--/ / / ANS
Hi again! On Monday 22 August 2005 02:47, David Abrahams wrote:
I am trying to do something like
python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1));
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that?
I tried some more ways (e.g. returning None), and found out that it's MyVector &result((python::extract<MyVector &>(args[0])())); which is not working. Your question indicates that it should be possible to initialize the object given as first parameter. If so, how should I do that? I need access to the contained C++ object, or do I have to export internal functions to python in order to set the state of the "self"-object? -- Ciao, / / /--/ / / ANS
On Monday 22 August 2005 15:33, Hans Meine wrote:
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that?
I tried some more ways (e.g. returning None), and found out that it's
MyVector &result((python::extract<MyVector &>(args[0])()));
which is not working. Your question indicates that it should be possible to initialize the object given as first parameter. If so, how should I do that? I need access to the contained C++ object, or do I have to export internal functions to python in order to set the state of the "self"-object?
Actually, I don't understand what kind of object I get in args[0]. Obviously, this corresponds to the "self"-argument of python __init__ functions. Naively, I would expect that it carries some kind of default-constructed C++ object (which I would like to prevent in the first place, but anyhow). But I fail to either extract<>() something useful from it, or manipulate the object through a TypeWrapper - e.g. I exported the vector's resize() function to python and do python::object result(args[0]); result.attr("resize")(size); However, I always get the same error messages - that I cannot do with a "PythonVector" what I am trying to do (extraction/call methods) because a "vigra::PythonVector<float>" is expected, which is exactly the class wrapped with class_ as "PythonVector": | ArgumentError: Python argument types in | PythonVector.resize(PythonVector, int) | did not match C++ signature: | resize(vigra::PythonVector<float> {lvalue}, unsigned) Maybe the "self"-object passed to the constructor is incomplete in some way? Ciao, / / /--/ / / ANS
Hi again! On Monday 22 August 2005 02:47, David Abrahams wrote:
I am trying to do something like
python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1));
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that?
I now tried to make my problem as clear as possible by createing a short testcase module (which also contains some comments summarizing the most important stuff from my other posts). # this works fine until.. from vector_testcase import * v1 = createVector(1,2,3) assert len(v1) == 3 v1[2]=42 # ..here. I cannot make the next line work: v2 = Vector(1,2,3) (I am getting: "TypeError: No registered converter was able to extract a C++ reference to type MyVector from this Python object of type Vector") The ultimate goal is to make "pickle.dumps(v1)" work - if you see a way without having a working python constructor, that would be OK, too. -- Ciao, / / .o. /--/ ..o / / ANS ooo
--- Hans Meine <hans_meine@gmx.net> wrote:
v1 = createVector(1,2,3)
I recommend against doing it that way. It is not compatible with the C++ interface: std::vector<double> a(1, 2); initializes a vector of size 1 with a value of 2. This looks and works best in my experience: vector([1,2,3]) Here is a full wrapper which works that way: http://cvs.sourceforge.net/viewcvs.py/cctbx/scitbx/include/scitbx/stl/vector... There are two tricks: 1. Also use scitbx/boost_python/container_conversions.h to define automatic Python list/tuple/range -> std::vector<> mappings: scitbx::boost_python::container_conversions::from_python_sequence< w_t, scitbx::boost_python::container_conversions ::variable_capacity_policy>(); where w_t is the "wrapped type", e.g. std::vector<double>. 2. Wrap the copy constructor: .def(init<w_t const&>()) Note that the vector_wrapper also supports pickling. Cheers, Ralf __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
On Thursday 25 August 2005 02:07, Ralf W. Grosse-Kunstleve wrote:
I recommend against doing it that way. It is not compatible with the C++ interface:
std::vector<double> a(1, 2);
initializes a vector of size 1 with a value of 2.
OK, you're right. I think it's a little bit misleading because I made up this testcase. My original work was on a 'Pixel' class for images with possibly multiple bands, so I like to write Pixel(red,green,blue) and not Pixel([red,green,blue]).
Here is a full wrapper which works that way:
http://cvs.sourceforge.net/viewcvs.py/cctbx/scitbx/include/scitbx/stl/vecto r_wrapper.h?view=markup
Thanks. BTW: For another vector-like class (Polygons consisting of points), I already chose your proposed interface. I tried vector_indexing_suite, but it's __getitem__ was around 100 times(!) slower than that of a python Polygon class storing a list of points! I found out that this is due to: - the support for slices (probably not very much slower) - the fact that python lists are optimized and already contain wrapped objects, whereas boost::python has to wrap each return value - the fact that vector_indexing_suite *proxies* the entries (IIUC, and then this is surely the biggest slowdown). Can you make a guess on the vector_wrapper performance in that respect? (I can live with a *small* slowdown on __getitem__ which is evened out elsewhere by much faster C++ algorithms like length() of a polygon.) Thanks again for your scitbx-links, I will try them out for my next vector-like class. Concerning my Pixel class, I will probably add some __init__s of varying arity (up to e.g. 5) by hand. -- Ciao, / / /--/ / / ANS
--- Hans Meine <meine@kogs1.informatik.uni-hamburg.de> wrote:
Can you make a guess on the vector_wrapper performance in that respect? (I can live with a *small* slowdown on __getitem__ which is evened out elsewhere by much faster C++ algorithms like length() of a polygon.)
I never did careful timings; in fact, no timings at all. My simple vector_wrapper doesn't do anything special. There isn't much opportunity for a slow-down. I don't think the positive_getitem_index() call adds much to the runtime.
Thanks again for your scitbx-links, I will try them out for my next vector-like class.
I thought of the scitbx vector_wrapper just as an example you could use as a start. It does things you don't want (the af::ref wrappers). I'd just look for ideas and possibly copy fragments.
Concerning my Pixel class, I will probably add some __init__s of varying arity (up to e.g. 5) by hand.
Sounds like a pragmatic solution. You could also wrap the pixel class as, say, "_pixel" and add a function with the name "pixel" using boost::python::raw_function. Cheers, Ralf __________________________________________________ Do You Yahoo!? Tired of spam? Yahoo! Mail has the best spam protection around http://mail.yahoo.com
Hi! I want to pick up the discussion again. Today, I discussed the problem (defining an __init__ which takes an arbitrary number of arguments) again with Ullrich Köthe, and thanks to his stamina when digging through the depths of boost::python, we now have a working solution. On Monday 22 August 2005 02:47, David Abrahams wrote:
I am trying to do something like
python::class_<MyVector>("PythonVector", python::no_init) .def("__init__", python::raw_function(&createVector, 1));
But this gives the vector as first element of the args tuple to createVector().
What's wrong with that?
We could not find out what to do with args[0] or what it actually contains. It's an object which is said to be of type "PythonVector" in error messages, but one cannot extract<> anything from it, and we could not find any other use for it. (See also my messages from the 22th of August) After unsuccessfully trying to work out how the callable objects created by make_constructor initialize this "self" argument, Ullrich's final idea was to just pass this argument from our raw constructor function to another one created by make_constructor. The rest of the arguments are passed as (sliced-off) tuple to a factory function (similar to what Ralf proposed back then). Attached, you find the our new make_constructor implementation in the .hpp file, and a small testcase in the .cpp. (Compile into rawconstructor.so) Use like this from Python: import rawconstructor -- Ciao, / / /--/ / / ANS
participants (4)
-
David Abrahams -
Hans Meine -
Hans Meine -
Ralf W. Grosse-Kunstleve