ctypes weird handling of variadic arguments in functions

Viewed 325

I am launching this snippet:

>>> from ctypes import *
>>> libc = CDLL("libc.so.6")
>>> libc.printf("%f %f\n", c_float(1.0), c_double(1.0))
0.000000 1.000000

printf expects a double for %f BUT I thought floats got promoted to doubles in variadic functions, as the following C code shows:

#include<stdio.h>
int main()
{
   float a = 1.0f;
   double b = 1.0;
   printf("%f %f\n", a, b);
}

produces the expected 1.000000 1.000000.

Am I missing something? Is the compiler doing some implicit casting in the C code?

I am using a 64-bits machine.

1 Answers

The caller is responsible for doing the type promotion from float to double. But ctypes doesn't know anything about the signature of printf unless you tell it. It doesn't know it's variadic and that type promotion is required.

If you don't tell ctypes what the signature of a function is by setting argtypes and restype, it assumes a int return type, and that whatever parameter you passed in match the signatures. So in your case it just assumes the signature of printf is:

int printf(char*, double, float)

As far as I know there is no way to define a variadic argtypes in ctypes, but it doesn't seem to do casting anyway, even if argtypes is set:

>>> from ctypes import *
>>> libc = CDLL("libc.so.6")
>>> libc.printf.argtypes = [c_char_p, c_double, c_double]
>>> libc.printf(c_char_p(b"%f %f\n"), c_double(1.0), c_float(1.0))
Traceback (most recent call last):
  File "printf.py", line 5, in <module>
    libc.printf(c_char_p(b"%f %f\n"), c_double(1.0), c_float(1.0))
ctypes.ArgumentError: argument 3: <class 'TypeError'>: wrong type
Related