Hi,

today I fixed hopefully the remaining bugs in Python <-> C++ interface:

* Use the Python() C++ class as the only interface to Python
* properly deallocated all memory when the Python() class is deleted
* fixed numerous segfaults related to that
* figured out how to pass your own (derived) C++ class to Python and
get its virtual methods called properly from Python
* figured out how to write the PY_NEW() function in Cython only,
without the need of the stdcython.h/c files (that we currently have in
hermes2d)
* wrote test cases to all of the above (that could also serve as
examples of usage, both in Python and in C++)

The code is here:

http://github.com/certik/hermes_common

Here is a (complete) example how to use scipy to print the sparse
matrices nicely (from C++):

    Python *p = new Python();
    p->push("m", c2py_CSRMatrix(this));
    p->exec("S = str(m.to_scipy_csr())");
    printf("%s\n", py2c_str(p->pull("S")));
    delete p;

It initializes the wrappers/python (so the user doesn't have to worry
about this at all  --- this used to be quite nontrivial!), pushes the
C++ matrix in (as a Python class, that's the "m" symbol), then
converts it to scipy, calls str() on the scipy matrix (so scipy prints
it), saves it to "S", then pulls the string "S" back to C++ and prints
it using C++ printf(). Finally it frees the Python and the namespace
(e.g. both "m" and "S" symbols will be freed).

Notice that you don't have to worry about memory allocation,
everything should just work. The py2c_* and c2py_* methods are
conveniently defined in Cython, e.g.:

cdef api char* py2c_str(object s):
    return s

cdef api object c2py_CSRMatrix(c_CSRMatrix *m):
    cdef CSRMatrix c
    c = <CSRMatrix>PY_NEW(CSRMatrix)
    c.thisptr = <c_Matrix *>m
    return c

and that's it, you don't have to redeclare it in C++, as Cython will
do that for you automatically. If one changes these api functions, one
has to run the convert_api.py script:

http://github.com/certik/hermes_common/blob/master/convert_api.py

that takes the standard Cython generated header file (that uses
static/local API) and creates a new .h and .cpp files that use the
global API, so that the user doesn't have to worry about this at all
in C++. So all is automatic, and later on, after we use it more in
hermes2d and hermes1d, and if all is ok, I'll see how cython could be
modified, so that it can produce these files itself.


It took me quite some time to figure everything out, but it's worthy.
It used to be a big pain to use Python from C++ (for nontrivial things
like the above, where you had to initialize python and the wrappers in
each cpp file...), but with this Python() C++ class, everything is
super easy now.

Let's say you have this array (in practice way longer of course):

    double a[3] = {1., 5., 3.};

and you want to print it nicely for debugging purposes. Here is the
complete code that you have to put in your program:

    Python p=Python();
    p.push("A", c2numpy_double(a, 3));
    p.exec("print A");

Which prints:

[ 1.  5.  3.]

Now compare this to the old way by hand:

    for (int i=0; i<3; i ++)
        printf("%f ", a[i]);
    printf("\n");

which prints:

1.000000 5.000000 3.000000

now imagine I want to just print the first 5, or last 5 numbers, or
just taking advantage of the nice way numpy prints large matrices (it
puts ... in the middle), then I can just use:

    p.exec("print 'Last two numbers:', A[-2:]");

etc.

It is all pretty trivial now, but it seems noone has done this before,
so it took me quite some time to figure everything out. So I am happy
with the results, thanks Robert, Lisandro and others for the help.

Ondrej
_______________________________________________
Cython-dev mailing list
[email protected]
http://codespeak.net/mailman/listinfo/cython-dev

Reply via email to