wrap c libraries with ctypes
cython is a powerful tool to integrate c and python code; but if the only usage is to call c library code from python, there are other tools available; one such tool is ctypes; ctypes is a foreign function library that provides c compatible data types and allows calling functions in shared libraries; ctypes is distributed with python itself, so there is no need to install it before use;
example 1
the first example is about an adder function written in c file add.c
:
int add(int a, int b)
{
return a + b;
}
to call this function in python, we first compile it into a shared library:
gcc -shared -fPIC -o libadd.so add.c
the we call it in python like this:
from ctypes import cdll
libadd = cdll.LoadLibrary('./libadd.so')
print(libadd.add(3, 4))
output:
7
this is even simpler than using cython, right? we made no declarations; we did
not have a setup.py
; we just opened that dll and called the function inside,
passing python objects as arguments;
as you can guess, there are auto type conversions behind the scene; ctypes
can
auto convert None
, integers, bytes and strs to corresponding c data types; for
a complete list of c data types defined by ctypes, follow this
link;
example 2
the second example involves calling sin
function from c library libm
;
since this library is not made by ourselves, we do not know where it is; but we
can find it using find_library
:
from ctypes.util import find_library
print(find_library("m"))
output:
libm.so.6
now we can open this library and use its functions (for brevity, we omit print
statements, same below):
libm = cdll.LoadLibrary('libm.so.6')
libm.sin(3.14/2)
but this gives us an error saying something like ctypes does not know how to convert the parameter;
here the c function sin
has prototype double sin(double x)
; specifically,
x
has c double
type, but we are passing an argument having python float
type; this is not one of ( None
, integers, bytes, strs ), so ctypes does not
auto convert it; we have to wrap it in its corresponding c data type; here we
convert python float
type into c double
type:
from ctypes import c_double
libm.sin(c_double(3.14/2))
this time it does not raise an error, but outputs 0
;
alright, this is another problem: by default functions are assumed to return the
c int
type; we need to set the restype
attribute of the function object to
specify another return type; so we do that:
libm.sin.restype = c_double
libm.sin(c_double(3.14/2))
output:
0.9999996829318346
now it works;
in fact, we can set the argtypes
attribute of the function object to specify
its argument types, like this:
libm.sin.argtypes = [ c_double ]
libm.sin(3.14/2)
output:
0.9999996829318346
note that we omitted c_double
in the argument list, but it still works because
this time the function knows about argument types;
summary
these examples should have illustrated the basic usage of ctypes
; of course it
can do more; there are many topics we did not cover here: arrays, structs,
unions, pointers, function pointers, etc.; ctypes supports all of them; more
details can be found in its documentation;