Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error message "Cannot convert <type_name>* to Python object" in Cython

Tags:

python

cython

When I cythonize the following Cython-module

%%cython
cdef double *ptr=[1,2,3]
print(ptr)

I get the following error message:

Cannot convert 'double *' to Python object

However, the following Cython module:

%%cython
cdef double val=0.0
print(val)

is cythonized without problems.

This problem occurs also for any other pointer type (i.e. int *, float * and so on).

like image 632
ead Avatar asked Jan 27 '23 00:01

ead


1 Answers

print is a pure Python function and in order to be able to call it, the arguments must be Python objects.

Obviously, neither ptr in the first Cython-module nor val in the second are python objects. There is however a difference: a cdef variable of type double can be converted to a Python-Float automaticly by Cython via PyFloat_FromDouble - and this is exactly what happens under the hood: Cython creates a temporary Python-Float from val, passes it to print and destroys it afterwards.

There are other types which can be automatically converted to Python objects: double, float, int & similar, but also std::vector and other c++-containers.

However, there is no such automatic conversion for raw-C-pointers (char *, Py_UNICODE* and their different reincarnations being an exception - see the example later on) - because it is not possible to do in the first place, as Cython doesn't know the length of the array (is it an array at all?) to which the pointer points.

The only solution in this case: one has to manually convert the content of the memory to a python list (or to whatever python-type it corresponds), here as example the function convert_to_python:

%%cython 
cdef convert_to_python(double *ptr, int n):
    cdef int i
    lst=[]
    for i in range(n):
        lst.append(ptr[i])
    return lst

cdef double *ptr=[1,2,3]
print(convert_to_python(ptr,3))

The most noteworthy thing is, that convert_to_python gets the number of elements as parameter - the information Cython is missing.

Btw: Cython would automatically convert C-arrays with known length to Python objects, for example:

%%cython 
cdef double arr[3] # length (=3) known to Cython 
arr[:]=[1,2,3]
print(arr)

compiles without problems.


char *, signed char *, unsigned char *, Py_UNICODE * and their const-variants are "special", because they can be automatically converted to either a bytes-object (char *) or unicode-object (Py_UNICODE *), assuming it is a null-terminated C-string (i.e. character \0 marks the end). Thus, it is possible to write code similar to

cdef char a[3] 
a = [90,0,90]   # ord('Z') = 90
print(a)        # char[3] decays to char *
# b"Z"

As one can see, the result is not b"Z\x00Z", because the string get automatically truncated at first `\0'. To ensure that the whole string converted to bytes object one has to specify length:

print(a[:3]) 
# b"Z\x00Z"

However, problem can arise if there is no \0 in the char-array, which could lead to segfaults (reads out of bounds).

like image 177
ead Avatar answered May 10 '23 04:05

ead