Home > OS >  Python to .cpp and back again with ctypes via an .so
Python to .cpp and back again with ctypes via an .so

Time:02-10

I'm doing something stupid, but not seeing what... I've reduced my problem to the below (absurd) example - but it highlights where my issue is - I'm clearly not able to pass data into and/or return from, c , coherently.

test.cpp

extern "C"
double reflect(double inp){
    return inp;
}

The above is compiled with:

g   -c -Wall - Werror -fPIC test.cpp -o testO.o
g   -shared -o testSO.so testO.o

Output file definitions are included only as I'd use them in real-problem

.py

import ctypes
hdl = ctypes.cdll.LoadLibrary(r"C:\Windows\path\to\testSO.so")

So, my returns

hdl.reflect(1)
>>> 1    (no ctypes conversion, but erm, OK)

hdl.reflect(1.1)
>>> Failure (this is expected)

hdl.reflect(ctypes.c_int(1))
>>> 1   (right well, c_int to c_double looks like it could implicitly work)

hdl.reflect(ctypes.c_float(1.1))
>>> 1006192077   (WTF?!?)

hdl.reflect(ctypes.c_double(1.2))
>>> 858993459    (WTF?!?!)

OS is windows 10, compiler is minGW-W64 8.1.0, python is 3.8 (anaconda)

I've ran DLLs before and I've compiled my own .c into python modules before - but for the life of me I cannot see what I'm doing wrong here.....!?!

edit: Corrected typo as pointed out by Topological Sort (path to .so was correct in code, was missing trailing " here)

CodePudding user response:

Object files and shared libraries do not contain any information about the types of function parameters or return types, at least not for C linkage functions.

Therefore ctypes has no way of knowing that the function is supposed to take a double argument and return a double.

If you call it with anything else than ctypes.c_double as argument, you will have undefined behavior. But you can set the argument types up for automatic conversion to the correct ctypes type (see below).

ctypes also assumes that the return type is int. If that is not the case, you are supposed to set the return types correctly before calling. In the following I also set up the argument types so that ctypes automatically uses the correct type in calls:

reflect = hdl.reflect
reflect.argtypes = [ctypes.c_double]
reflect.restype = ctypes.c_double
reflect(1.2)
  • Related