How to use a type macro from a c header? - cython

In an external library is a macro to a type:
typedef TCHAR MY_TYPE;
#define MY_MACRO_TYPE MY_TYPE;
At any place in the library the macro is used instead of the type.
So how can I provide this macro for cython (cdef extern MY_MACRO_TYPE variable)?
I tried to use it like it is:
cdef extern from "my_lib.h":
ctypedef TCHAR MY_TYPE
cdef MY_MACRO_TYPE MY_TYPE
Which throws the following error:
Error compiling Cython file:
------------------------------------------------------------
...
ctypedef TCHAR MY_TYPE
cdef MY_MACRO_TYPE MY_TYPE
^
------------------------------------------------------------
src/pytypes.pxd:22:9: 'MY_MACRO_TYPE' is not a type identifier
warning: src/pytypes.pxd:22:22: 'MY_TYPE' redeclared

While I wrote this question, I found a possible solution, which may help anyone else.
Cause cython just needs to know the type, you can ignore the macro and use a typedef alias instead.
So it looks like:
cdef extern from "my_lib.h":
ctypedef TCHAR MY_TYPE
ctypedef MY_TYPE MY_MACRO_TYPE
Which works for me, without any errors.

Related

Cython compilation warning incompatible types

I am trying to use a c DLL in cython and during the compilation phase I get a warning from the C compiler :
warning C4133: "=" : incompatible types - from 'foobar *' to 'foobar *'.
My pxd looks like this :
#!/usr/bin/env python3
#cython: language_level=3
cdef extern from "typedef.h"
struct foobar:
long *index
double *my_array
int value
cdef extern from "functions.h"
foobar *get_foobar(char *name);
And my pyx like that :
cimport pxd_file_name
cdef class Handler:
cdef pxd_file_name.foobar *__foobar
def load_foobar(self, char *name):
self.__foobar = pxd_file_name.get_foobar(name) <==
def another_method(self):
pass
I got the warning because of the line marked by an arrow and I don't understand why.
Is there a way to fix this ?
I manage to found my mistake.
Because in my .h file, my struct was declared using typedef, I had to write ctypedef struct foobar instead of struct foobar in my pxd file

strncat use in cython

for learning purposes I am trying to use strncat in cython within a class.
I do:
from libc.stdlib cimport free
cdef extern from "<string.h>":
char *strncat(char *pto, const char **pfrom, size_t_size)
size_t strlen (const char *s)
cdef class test:
cdef:
char *palabra
#size_t len_palabra
def __cinit__(self, char *Palabra):
self.palabra = Palabra
cdef append_palabra(self, char *other_palabra):
strncat(self.palabra, &other_palabra, strlen(other_palabra))
print(other_palabra)
def manipulate_palabra(self, char *other_palabra):
self.append_palabra(other_palabra)
print(self.palabra)
def __dealloc__(self):
free(self.palabra)
a = test(b'first ')
a.manipulate_palabra(b'second')
The print statements throw:
b'second'
b'first H\xc3-\xcd\xa9\x7f'
How should I obtain b'first second' ?
I purposely bring up the whole class to hear critics and comments as I am almost illiterate in Cython.
Thank you!
Your issue was that you'd defined:
char *strncat(char *pto, const char **pfrom, size_t_size)
but the actual signature is
char *strncat(char *pto, const char *pfrom, size_t_size)
I suspect this should have at least generated a warning at the C compilation stage that it's worth taking seriously.
Cython does wrap most of the C standard library so the simplest solution would be
from libc.string cimport strncat, stolen
There's nothing magical about the Cython wrappings - they're provided for convenience but the don't do anything that you can't write yourself (at least for C, this isn't quite true for the C++ wrappings). However the good thing about them is that they are tested, so you can be reasonably confident that they're right.

How to do a ctypedef with fused dtype

I have the following typedef
ctypedef fused sa_t:
np.int32_t
np.int64_t
In a function with a sa_t argument, I have the following declaration
cdef vector[pair[sa_t, sa_t]] count
I would like to make pair[sa_t, sa_t] more accessible.
However, I cannot do a ctypedef in the function and my attempts to do one outside don't work.
The following demo works fine(Cython version 0.29.13), does it give you some hints?
from libcpp.vector cimport vector
from libcpp.pair cimport pair
cimport numpy as np
ctypedef fused sa_t:
np.int32_t
np.int64_t
ctypedef pair[sa_t, sa_t] sa_pair_t
ctypedef vector[sa_pair_t] sa_pairs_t
cdef void func(sa_t value):
cdef sa_pairs_t sa_pairs
sa_pairs.push_back(pair[sa_t, sa_t](value, value))
print(sa_pairs)
func[np.int32_t](1) # print [(1, 1)]

can I use a typedef in #cython.locals

foo.pxd:
cdef class cls: pass
ctypedef int typ
main.pxd:
from foo cimport cls, typ
main.pyx:
import cython
#cython.locals(a='cls')
def f(a): pass
#cython.locals(b='typ') # doesn't compile
def g(b): pass
Cython compiles function f() but not function g(), saying 'Not a type.'
If I remove the quotes from 'typ' then it will compile, but no longer run in the interpreter.
It's important to me that any solution works compiled AND interpreted.

"Storing unsafe C derivative of temporary Python reference" when trying to access struct pointer

I want to use a library that gives me a dynamic array. The dynamic array struct has a property void* _heap_ptr which gives the start of the array.
After having built the list, I want to access this pointer in cython (to make a copy of the array). But I cannot seem to get the pointer element from the struct.
Here is my pyx:
cimport src.clist as l
def main():
cdef l.ptr_list basic_list
cdef int i = 42
basic_list = l.create_list_size(sizeof(i), 100)
l.list_add_ptr(basic_list, &i)
cdef int* arr;
arr = basic_list._heap_ptr
for i in range(1):
print(arr[i])
This is the error message:
Error compiling Cython file:
------------------------------------------------------------
...
l.list_add_ptr(basic_list, &i)
cdef int* arr;
arr = basic_list._heap_ptr
^
------------------------------------------------------------
src/test.pyx:14:20: Cannot convert Python object to 'int *'
Error compiling Cython file:
------------------------------------------------------------
...
l.list_add_ptr(basic_list, &i)
cdef int* arr;
arr = basic_list._heap_ptr
^
------------------------------------------------------------
src/test.pyx:14:20: Storing unsafe C derivative of temporary Python reference
And my pxd:
cdef extern from "src/list.h":
ctypedef struct _list:
void* _heap_ptr
ctypedef struct ptr_list:
pass
ptr_list create_list_size(size_t size, int length)
list_destroy(ptr_list this_list)
void* list_at_ptr(ptr_list this_list, int index)
list_add_ptr(ptr_list this_list, void* value)
How can I fix my code? Why is this happening? From my investigations that error message pops up if you have forgotten to declare something as C (ie. use malloc not libc.stdlib.malloc, but I cannot see that anything similar is happening here.)
There are two issues in your code.
First: struct ptr_list has no members and thus no member _heap_ptr. It probably should have been
ctypedef struct ptr_list:
void* _heap_ptr
Cython's error message is not really helpful here, but as you said it pops up usually when a C-declaration is forgotten.
Second: you need to cast from void * to int * explicitly:
arr = <int*>basic_list._heap_ptr