0

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)
Derik
  • 57
  • 2
  • 7
  • 3
    You can cast any pointer to void*. The question is what the c function expects as input - just working memory, something else? – ead Sep 13 '20 at 06:31
  • @ead The `ida_mem` is a type pointer to struct `IDAMemRec` which consists of various parameters and functions for numerical solver. https://github.com/swsyoon/Cython_test/blob/master/IDA1.0/src/ida.h – Derik Sep 13 '20 at 17:20
  • 3
    I'd suggest you might want a wrapper class that encapsulates `IDAMemRec`. Look at [the documentation for wrapping C++ classes](https://cython.readthedocs.io/en/latest/src/userguide/wrapping_CPlusPlus.html#create-cython-wrapper-class) - I know what you're doing isn't C++ but you could take a similar approach. – DavidW Sep 13 '20 at 18:09
  • @DavidW Thank you. Is there any good reference or code for me to learn how to write the wrapper class for a function with void pointer argument ? – Derik Sep 14 '20 at 02:26
  • 2
    Based on the documentation in the header you had posted, you could create a cdef class (aka Extension type aka your wrapper class that would be accessible in python) which has a cdef-ed property of type `IDAMem` (e.g. called `ida`). In the `__cinit__` function for the cdef class, you would initialize `ida` with a call to `IDAMalloc` and in the corresponding `__dealloc__` call, you would need to free `ida` with `IDAFree`. – CodeSurgeon Sep 14 '20 at 05:21
  • @CodeSurgeon Thank you. I will try to write something based on your suggestion. – Derik Sep 14 '20 at 18:17

1 Answers1

0

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
sirlark
  • 2,187
  • 2
  • 18
  • 28