CFFI

CFFI is an FFI for Python that is an interesting alternative to ctypes. It is not a part of the standard library but it is easily available as a PyPI as the cffi package. It is different from ctypes because it puts more emphasis on reusing plain C declarations instead of providing extensive Python APIs in a single module. It is way more complex and also has a feature that allows you to automatically compile some parts of your integration layer into extensions using C compiler. So it can be used as a hybrid solution that fills the gap between plain C extensions and ctypes.

Because it is a very large project, it is impossible to briefly introduce it in a few paragraphs. On the other hand, it would be a shame to not say something more about it. We have already discussed one example of integrating the qsort() function from the standard library using ctypes. So, the best way to show the main differences between these two solutions would be to reimplement the same example with cffi. I hope that the following one block of code is worth more than a few paragraphs of text:

from random import shuffle 
 
from cffi import FFI 
 
ffi = FFI() 
 
ffi.cdef(""" 
void qsort(void *base, size_t nel, size_t width, 
           int (*compar)(const void *, const void *)); 
""") 
C = ffi.dlopen(None) 
 
 
@ffi.callback("int(void*, void*)") 
def cffi_int_compare(a, b): 
    # Callback signature requires exact matching of types. 
    # This involves less more magic than in ctypes 
    # but also makes you more specific and requires 
    # explicit casting 
    int_a = ffi.cast('int*', a)[0] 
    int_b = ffi.cast('int*', b)[0] 
    print(" %s cmp %s" % (int_a, int_b)) 
 
    # according to qsort specification this should return: 
    # * less than zero if a < b 
    # * zero if a == b 
    # * more than zero if a > b 
    return int_a - int_b 
 
 
def main(): 
    numbers = list(range(5)) 
    shuffle(numbers) 
    print("shuffled: ", numbers) 
 
    c_array = ffi.new("int[]", numbers) 
 
    C.qsort( 
        # pointer to the sorted array 
        c_array, 
        # length of the array 
        len(c_array), 
        # size of single array element 
        ffi.sizeof('int'), 
        # callback (pointer to the C comparison function) 
        cffi_int_compare, 
    ) 
    print("sorted:   ", list(c_array)) 
 
if __name__ == "__main__": 
    main() 

The output will be similar to one presented earlier when discussing the example of C callbacks in ctypes. Using CFFI to integrate qsort in Python doesn't make any more sense than using ctypes for the same purpose. Anyway, the preceding example should show the main differences between ctypes and CFFI regarding handling datatypes and function callbacks.