1. Python CFFI
  2. Untitled project
  3. cffi
  4. Issues
Issue #73 resolved

Pointer types which differ from long/int via 32/64-bit are impossible to get warnings-clean on OS X (due to fat binary builds)

Glyph
created an issue

There's currently no way to express a conditional 'typedef' in the .cdef section of an FFI.

This is problematic, among other reasons, because on OS X, when building fat binaries (the default for the system Python), when you run 'cc', the compiler is effectively invoked twice.

Consider the attached program longint.py, and this output that it produces when building its extension:

clang: warning: argument unused during compilation: '-mno-fused-madd'
__pycache__/_cffi__x2fa1458bx77425ad8.c:177:29: warning: incompatible pointer
      types passing 'int (*)(void)' to parameter of type
      'AFunctionReturningInteger' (aka 'AnIntegerWith32Bits (*)()')
      [-Wincompatible-pointer-types]
  { result = InvokeFunction(x0); }
                            ^~
__pycache__/_cffi__x2fa1458bx77425ad8.c:160:62: note: passing argument to
      parameter 'f' here
AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) {
                                                             ^
1 warning generated.
7

This is especially problematic because one of the very, very common types throughout OS X's API is "OSStatus", which is defined much like that - 'int' in 64, 'long' on 32, so wrapping any C APIs which use function pointers on this platform results in this sort of warning.

One workaround is to set the ARCHFLAGS environment variable to skip building 32-bit versions of things, and make sure that the 64-bit versions at least are warnings-clean; e.g. ARCHFLAGS='-arch x86_64' python longint.py. Obviously if you still want to run on 32-bit that is a problem.

Another workaround would be to set extra_compile_args=["-Wno-incompatible-pointer-types"]. Hopefully I don't have to explain why that isn't ideal :).

A third workaround is to make some 'void *' typedefs that you use when declaring arguments but not defining the callbacks, so those specific callbacks won't be called out, without disabling incompatible pointer type warnings entirely.

Comments (8)

  1. Glyph reporter

    It would? I guess I might not understand the mechanics of "..." exactly. In this case I need to return the thing that is typedef'd to "..."; in that case, can cffi know how to marshal my integer to the right type properly?

  2. Armin Rigo

    I mean that the problem would be resolved if we had a way to specify OSStatus as being "an integer of some unknown size" and let CFFI figure out automatically what size it has (including differencing between "int" and "long" even when they have the same size). But that's not obvious at the level of the implementation.

    A related issue is that "int32_t" is assumed to be "int" on 32-bit, even though it may actually be "long" on some platforms, given that it's also a 32-bit type. This might cause the same problems.

    A possible "workaroundish" solution would be for CFFI to define types like "_cffi_small_int32_t" and "_cffi_big_int32_t", which would be respectively the first or last in the list "char, short, int, long, long long" that is 32 bit wide. Then OSStatus would be "_cffi_big_int32_t" on both 32- and 64-bit.

  3. Glyph reporter

    It does not work for the example cited in longint.py.

    If I change the typedef to have int... instead of just int, I get this traceback:

    Traceback (most recent call last):
      File "longint.py", line 20, in <module>
        """)
      File ".../cffi/api.py", line 373, in verify
        lib = self.verifier.load_library()
      File ".../cffi/verifier.py", line 95, in load_library
        self._write_source()
      File ".../cffi/verifier.py", line 170, in _write_source
        self._write_source_to(f)
      File ".../cffi/verifier.py", line 160, in _write_source_to
        self._vengine.write_source_to_f()
      File ".../cffi/vengine_cpy.py", line 80, in write_source_to_f
        self._generate("decl")
      File ".../cffi/vengine_cpy.py", line 212, in _generate
        method(tp, realname)
      File ".../cffi/vengine_cpy.py", line 406, in _generate_cpy_function_decl
        self._convert_expr_from_c(tp.result, 'result', 'result type'))
      File ".../cffi/vengine_cpy.py", line 315, in _convert_expr_from_c
        raise NotImplementedError(tp)
    NotImplementedError: <AnIntegerWith32Bits>
    
  4. Log in to comment