Wrapping C function with void pointer as argument using Cython

525 views Asked by At

I am learning C library wrapping in cython. I compiled a couple of simple C function and header files using cython and now am trying to run another example which is more complicated one than previous examples.

I downloaded very first version of Sundials C source code (IDA module only) and made a *.lib using VS 2019. Now I am trying to wrap just one function to see how it works. However I couldn't figure out how to properly wrap a function with void * argument to cython function. Here is the example.

The ida_mem is void pointer to an address of an allocated memory from C malloc function. How should I call IDACalcIC function at the bottom with correct pointer argument ?

IDA.pyx file


cdef extern from "ida.h":
    
    ctypedef double real;
    ctypedef int integer;
    int IDACalcIC(void *ida_mem, int icopt, real tout1, real epicfac, int maxnh, int maxnj, int maxnit, int lsoff, real steptol)

            
cpdef CyIDACalcIC(void *ida_mem, icopt, tout1, epicfac, maxnh, maxnj, maxnit, lsoff, steptol):
    
    cdef int icopt
    cdef int maxnh
    cdef int maxnj
    cdef int maxnit
    cdef int lsoff
    cdef real tout1
    cdef real epicfac
    cdef real steptol
    
    IDACalcIC(ida_mem (?), icopt,  tout1,  epicfac, maxnh,  maxnj,  maxnit, lsoff, steptol)
1

There are 1 answers

0
sirlark On

The void pointer you're passing is is just working memory for the solver, which you need to allocate yourself prior to calling other functions that might use it, typically the solving function. This is typical in C where you manage memory yourself, but quite alien if in Python.

You're looking to do the Cython equivalent of this code in pySundials (lines 94-105 and 147-153)

You can allocate the memory yourself with a call to IDACreate, and then pass the returned value into IDACalcIC, although I would suggest using wrapper classes similar to those below to do it, which will make using the C library feel more natural to python user's.

The IdaMemObj class exists to wrap the allocated memory as returned by the C function IDACreate in the form of a void *. It uses __del__ to ensure the memory allocated is freed (dealloacted) when the python object goes out of scope and is garbage collected.

# PySUNDIALS: trunk/2.5.0/src/ida.py lines 94-105
class IdaMemObj(object):
    def __init__(self, obj):
        self.obj = obj
        self.dealloc = False

    def __del__(self):
        if self.dealloc:
            p = ctypes.c_void_p()
            p.value = self.obj
            ida.IDAFree(ctypes.byref(p))
ida.IDAFree.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
ida.IDAFree.restype = None
# PySUNDIALS: trunk/2.5.0/src/ida.py lines 147-153
def IDACreate():
    obj = ida.IDACreate()
    if obj == None:
        raise AssertionError("SUNDIALS ERROR: IDACreate() failed - returned NULL pointer")
    return IdaMemObj(obj)
ida.IDACreate.argtypes = []
ida.IDACreate.restype = ctypes.c_void_p