Avalokitesvara
Avalokitesvara

Reputation: 97

Passing Structs in Ctypes

I have been trying to pass a struct in Ctypes. But the function call is throwing a format error.

This is my C function:

typedef struct Point2Struct {   
    double x, y;
    } Point2;

Point2 test(Point2 k)
{
  return k;
}

The python call is as follows:

class Point2(Structure):
    _fields_ = [('x',c_double),('y',c_double)]

lib.test.argtypes=[Point2]
lib.test.restype=Point2
p = Point2(1.1, 2.2)

g = lib.test(p)

print g.x, g.y

When I call the function through CDLL, I get:

ValueError: Procedure called with not enough arguments (4 bytes missing) or wrong calling convention

with WinDLL, I get:

ValueError: Procedure probably called with too many arguments (16 bytes in excess)

I compiled the C code into a DLL using (Mingw) gcc under Windows 7.

 gcc -shared -o test.dll test.o

I also tried using a .so file with:

gcc -shared -Wl,-soname,test-o test.so -fPIC test.c

But I get the same errors.

What am I doing wrong ? Should I compile with any specific option ?

Upvotes: 2

Views: 1596

Answers (1)

Eryk Sun
Eryk Sun

Reputation: 34260

To handle aggregate returns larger than 8 bytes, both MinGW and Microsoft's compiler implicitly pass a pointer to local memory in the caller. It looks like your version of gcc defaults to popping this pointer in the callee. However, ctypes/libffi was built with Visual Studio and expects Microsoft's convention, which handles this in the caller. That's why it complains about missing 4 bytes.

If you're using gcc 4.6+, there's a function attribute to enable Microsoft's convention:

callee_pop_aggregate_return (number)

On 32-bit i?86-- targets, you can control by those attribute for aggregate return in memory, if the caller is responsible to pop the hidden pointer together with the rest of the arguments - number equal to zero -, or if the callee is responsible to pop hidden pointer - number equal to one. The default i386 ABI assumes that the callee pops the stack for hidden pointer.

It seems gcc 4.7 uses the Microsoft convention as the default for Windows targets:

Note, that on 32-bit i386 Windows targets the compiler assumes that the caller pops the stack for hidden pointer.

With my test using gcc 4.6.3 on Windows, it still used the "default i386 ABI". Setting the function attribute solved the problem:

Point2 test(Point2 k)
    __attribute__((callee_pop_aggregate_return(0)));

Your mileage may vary, of course. Better still, if you're in control of the spec I think it's simpler to just use an explicit output parameter passed by reference.

Upvotes: 3

Related Questions