How to pass references of declared variables to C function in python using ctypes

Question:

I would like to call a C function from python. This C function is void, thus the "return parameters" (data I want to change) are defined as pointers in the C function’s definition.

The function in C looks like this (note: I cannot and will not change it as it is generated automatically by MATLAB codegen):

void func(int   *input_var,    // input
          float *output_var    //output
         ) {
    ... 
}

from python, I am calling my function as so

import ctypes as C

func = C.CDLL('path/to/lib.so').func

input_var = C.c_int(5)
output_var = C.POINTER(C.c_float) # basically want to just declare a pointer here
func(C.byref(input_var), C.byref(output_var))

The error I get is

TypeError: byref() argument must be a ctypes instance, not '_ctypes.PyCPointerType'

if I remove bref() I get

ctypes.ArgumentError: argument 2: <class 'TypeError'>: Don't know how to convert parameter 2

I also tried to pass in output_var as C.byref(output_var()); this leads to a Segmentation fault (core dumped)

Asked By: Brian Barry

||

Answers:

you can use the .byref directly after declaring the variable no need to make a pointer.

so in your example, you can declare the output as the variable type you need it to be and then pass that by reference to the function.

 output_var = C.c_float()
 func(C.byref(input_var), C.byref(output_var)

this should work.

Answered By: Juan

Listing [Python.Docs]: ctypes – A foreign function library for Python.

Do things like you’d do it from C (and you’re already doing for the input argument): declare a float variable and pass its address to the function (via byref).

One important thing: check [SO]: C function called from Python via ctypes returns incorrect value (@CristiFati’s answer) for a common pitfall when working with CTypes (calling functions).

Example (simplest scenario: pointers wrapping single values, if there were arrays, or function performed some memory allocations, things would be a bit more complex).

  • dll00.c:

    #include <stdio.h>
    
    #if defined(_WIN32)
    #  define DLL00_EXPORT_API __declspec(dllexport)
    #else
    #  define DLL00_EXPORT_API
    #endif
    
    
    #if defined(__cplusplus)
    extern "C" {
    #endif
    
    DLL00_EXPORT_API void dll00Func00(int *pIn, float *pOut);
    
    #if defined(__cplusplus)
    }
    #endif
    
    
    void dll00Func00(int *pIn, float *pOut)
    {
        if (pIn)
            printf("C - In: %dn", *pIn);
        if (pOut) {
            float f = 3.141593 * (pIn ? *pIn : 1);
            printf("C - Setting out to: %.3fn", f);
            *pOut = f;
        }
    }
    
  • code00.py:

    #!/usr/bin/env python
    
    import ctypes as cts
    import sys
    
    
    DLL_NAME = "./dll00.{:s}".format("dll" if sys.platform[:3].lower() == "win" else "so")
    
    
    def main(*argv):
        dll = cts.CDLL(DLL_NAME)
        func = dll.dll00Func00
        func.argtypes = (cts.POINTER(cts.c_int), cts.POINTER(cts.c_float))
        func.restype = None
    
        i = cts.c_int(2)
        f = cts.c_float(0)
    
        res = func(cts.byref(i), cts.byref(f))
        print("Values after calling function: {:d}, {:.3f}".format(i.value, f.value))
    
    
    if __name__ == "__main__":
        print("Python {:s} {:03d}bit on {:s}n".format(" ".join(elem.strip() for elem in sys.version.split("n")),
                                                       64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        rc = main(*sys.argv[1:])
        print("nDone.n")
        sys.exit(rc)
    

Output:

(qaic-env) [cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q075393602]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> ls
code00.py  dll00.c
[064bit prompt]>
[064bit prompt]> gcc -shared -o dll00.so dll00.c
[064bit prompt]>
[064bit prompt]> python ./code00.py
Python 3.8.10 (default, Nov 14 2022, 12:59:47) [GCC 9.4.0] 064bit on linux

C - In: 2
C - Setting out to: 6.283
Values after calling function: 2, 6.283

Done.
Answered By: CristiFati
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.