Super constructor in cdef classes - constructor

I am working on wrapping a C library using cython. I would like to use a cdef class to wrap a simple struct. The struct does require some parameters to be properly initialized. I put the corresponding code int the __cinit__ method like so:
cdef class Func:
cdef library.Func* func
def __cinit__(self, int value):
library.init_func(&self.func, value)
In python code I can create a function object via f = Func(17). The code then handles the initialization just fine. My question is the following: Say I want to extend the Func class in python (class MyFunc(Func): ...). How do I add a constructor to to MyFunc?
I would like to write a constructor with other parameters which calls
__cinit__(self, int value) with a value derived from constructor parameters. But it does not seem to be possible (likely for good reason) to call __cinit__ from python code.
Is there some way to include constructors in subclasses or should I create a wrapper around the function and delegate methods / properties?

Related

Should the reference count of PyObject* created by a cdef public function be manually decreased

I want to export a python class to be used in C++:
class Object:
def method(self):
pass
cdef public object create_object() with gil:
return Object()
cdef public void py_method(object obj) with gil:
obj.method() # obj is expected to have method()
Then the generated header file will have the following functions:
__PYX_EXTERN_C PyObject *create_object(void);
__PYX_EXTERN_C void py_method(PyObject *);
My question is: should I also have a function like
from cpython cimport Py_XDECREF, PyObject
cdef public void destroy_object(PyObject* obj) with gil:
Py_XDECREF(obj)
to avoid memory leak?
Then these functions may be used in C++ code like:
auto obj = create_object()
py_method(obj)
// Finally
destroy_object(obj)
Yes you should decrement it when calling it from C++. You could do it the way you propose or you could skip the definition of destroy_object and just use Py_XDECREF in C++.
Other things you should consider:
create_object can return NULL if it's raised a Python exception so check for that
RAII wrappers are always a good idea (to handle the reference counting)

Can Python object be referenced under cdef function?

In the code below, self is a python object, as it is declared from def instead of cdef. Can a python object be referenced under a cdef function, just like how it is used under the c_function from my example below?
I am confused because cdef makes it sound like cdef is a C function, so I am not sure if it is able to take on a python object.
class A(self, int w):
def __init__():
self.a = w
cdef c_function (self, int b):
return self.a + b
Thank you,
I am the OP.
From "Cython: A Guide for Python Programmers":
Source: https://books.google.com.hk/books?id=H3JmBgAAQBAJ&pg=PA49&lpg=PA49&dq=python+object+cdef+function&source=bl&ots=QI9If_wiyR&sig=ACfU3U1CmEPBaEVmW0UN1_9m9G8B9a6oFQ&hl=en&sa=X&ved=2ahUKEwiBgs3Lw5vqAhXSZt4KHTs0DK0Q6AEwBHoECAoQAQ#v=onepage&q=python%20object%20cdef%20function&f=false
"[...] Nothing prevents us from declaring and using Python objects and dynamic variables in cdef functions, or accepting them as arguments"
So I take this as the book saying "yes" to my original question.

Cython - copy constructors

I've got a C library that I'm trying to wrap in Cython. One of the classes I'm creating contains a pointer to a C structure. I'd like to write a copy constructor that would create a second Python object pointing to the same C structure, but I'm having trouble, as the pointer cannot be converted into a python object.
Here's a sketch of what I'd like to have:
cdef class StructName:
cdef c_libname.StructName* __structname
def __cinit__(self, other = None):
if not other:
self.__structname = c_libname.constructStructName()
elif type(other) is StructName:
self.__structname = other.__structname
The real problem is that last line - it seems Cython can't access cdef fields from within a python method. I've tried writing an accessor method, with the same result. How can I create a copy constructor in this situation?
When playing with cdef classes, attribute access are compiled to C struct member access. As a consequence, to access to a cdef member of an object A you have to be sure of the type of A. In __cinit__ you didn't tell Cython that other is an instance of StructName. Therefore Cython refuses to compile other.__structname. To fix the problem, just write
def __cinit__(self, StructName other = None):
Note: None is equivalent to NULL and therefore is accepted as a StructName.
If you want more polymorphism then you have to rely on type casts:
def __cinit__(self, other = None):
cdef StructName ostr
if not other:
self.__structname = c_libname.constructStructName()
elif type(other) is StructName:
ostr = <StructName> other
self.__structname = ostr.__structname

c++ class in fused type

I wish to implement python wrapper for a bunch of c++ classes. Somewhere in pxd I have:
cdef cppclass FooImpl1:
FooImpl1()
int foo()
cdef cppclass FooImpl2
FooImpl2()
int foo()
I wonder if I can write something like this in pyx python wrapper:
ctypedef fused FooImpl:
FooImpl1*
FooImpl2*
cdef class Foo:
cdef FooImpl impl
def __cinit__(self, int selector):
if selector == 1:
self.impl = new FooImpl1()
else:
self.impl = new FooImpl2()
def func(self):
# depending on the object stored in impl FooImpl2::foo or FooImpl1::foo
# will be called
return self.impl.foo()
Is there a way to accomplish expected behavior? FooImpl1 and FooImpl2 don't share abstract interface, they are template specializations of a class.
As of this version (0.20), Cython doesn't support fused types in classes, only in function parameters and variables. Here are the docs.

Unable to declare libcpp.map in the declarations file

I want to store C++ objects in a libcpp.map but I can't get it working. It even doesn't work on simple integers if I declare the map in the declarations file.
.pxd file:
from libcpp.map cimport map
cdef class MyClass:
cdef map[int,int] store
.pyx file:
cdef class MyClass:
def __cinit__(self):
self.store = map[int,int]()
Following error:
cdef map[int,int] store
^
C++ classes not allowed as members of an extension type,
use a pointer or reference instead
Why is this not working? If I declare it inside a function it's working fine.
going from the error it seems you need to store a pointer to it and invoke a new version on the heap
so
cdef map[int,int] *store
self.store = new map[int,int]()