Piotr Lopusiewicz
Piotr Lopusiewicz

Reputation: 2594

Order of arguments matter when calling C function using ctypes

Source of my C dll:

#include<stdio.h>

typedef struct {
    int nums[5];
    int tp;
} Sample;

float read_float(Sample s, float* arg){
    return arg[1];
} 

Source of Python code calling it:

from ctypes import *

class PySample(Structure):
    _fields_ = [("nums", c_int * 5),
                ("tp", c_int)]

if __name__ == "__main__":
    libp = CDLL(r"PathToMyDLL")
    rf = libp.read_float
    rf.restype = c_float
    s = PySample()
    for i in range(5):
        s.nums[i] = (11,22,33,44,55)[i]
    s.tp = 101
    print(rf(s, (c_float*3)(0.4, 0.5, 0.6)))

After compiling with gcc -shared and running the Python code I get some random small number. However if I change signature of C function to:

float read_float(float* arg, Sample s)

and corresponding Python print call to:

print(rf((c_float*3)(0.4, 0.5, 0.6), s))

(that is changing order of arguments in both definition and function call) then I get correct 0.5

That isn't desired behavior. Any ideas what I am doing wrong here and why it is happening?

(Python 3.4.1 for 64bit Windows and C code is compiled with gcc 4.8.1 (MinGw-W64))

Upvotes: 1

Views: 359

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 178021

If you add an argtypes declaration, your examples work correctly when compiled 32-bit and used with 32-bit Python:

rf.argtypes = [PySample, POINTER(c_float)]

But in 64-bit Python, if fails. I found that changing the argtypes to use a pointer to the structure made it work, even though C is passing the structure by value.

rf.argtypes = [POINTER(PySample), POINTER(c_float)]

BUT, If I changed your functions to modify the structure, it modified it in Python as well. Calling the function from C and actually passing by value did not modify the structure in the caller as expected, so this seems like a bug in 64-bit ctypes.

Upvotes: 2

Related Questions