Call C Code With Python
before you read, its good to know:
sometimes we may want to call C functions from python, because python may be:
- limiting
- slow
- wont have those functions
- we dont want to rewrite those functions again in python
with ctypes we can import shared C libraries, this give us almost all libc functionality
(or any other shared library that we may want to load)
how C and python handle the same data type
C and python are 2 different languages, so they handle the same data type differently, also python is a dynamiclly typed language, while C is staticlly typed language, so we can’t just directly pass arguments from python to C and vice versa, so before passing the values to the C function, python does Marshalling (in short, convert data from format A to format B)
(for this explanation let us assum that the C bytes values are correct)
C stores data in the smalles unit possible, integer is just 4 bytes, character is 1 byte, string is a list of
characters that the end of the string defined by the null terminator ‘\0’
in python everything is an object, integer is object, string is object, float is object, even functions in reality are objects, the length of the object in bytes can vary from system to system, but this is hidden from the python programmer
so you see the problem?, python stores everything as objects, while C doesnt even have objects, only the “raw” form of the value
for that, before we pass some values to the C functions from python, we may need to specifiy what value we are passing, so python can convert it to C data type structure
import libc in python and use it
py code snippet start
import ctypes
libc_name = "libc.so.6"
libc = ctypes.CDLL(libc_name)
def main():
language = b"python"
libc.printf(b"Hello world from %s (%S)\n", language, libc_name)
if __name__ == '__main__':
main()py code snippet end
we imported the libc with ctypes.CDLL function by providing its name, we can also give
the absolute path to the shared library if we want to use some shared library that does not exists in /etc/ld.so.conf
python3 document, how to use ctypes
create your own shared library, and call functions with python
project file structure
code snippet start
|-- main.py
`-- mylib
`-- mylib.c
code snippet end
c code snippet start
// mylib.c
#include <stdio.h>
float my_func(int i, float f){
printf("i=%d, f=%f\n", i, f);
return (float) i + f;
}c code snippet end
function takes integer and float, print them to the scree, and combie them,
now lets compile our new lib to object file and then create the shared library
(this can be done in one command, but for readability and understandability, we split it to 2 seperate commands)
sh code snippet start
> gcc -c -fpic mylib.c
> gcc -shared mylib.o -o libmylib.sosh code snippet end
now we can import our lib with the main.py and use this function like so
py code snippet start
import os
import ctypes
mylib = ctypes.CDLL(os.path.join('mylib', 'libmylib.so'))
def main():
i = 3
f = 0.5
print(mylib.my_func(i, f))
if __name__ == '__main__':
main()py code snippet end
if we run this python code we will get
code snippet start
> python3 main.py
Traceback (most recent call last):
File "/home/foo/main.py", line 15, in <module>
main()
File "/home/foo/main.py", line 12, in main
print(mylib.my_func(i, f))
ctypes.ArgumentError: argument 2: <class 'TypeError'>: Don't know how to convert parameter 2
code snippet end
this is happend because python dont know to what to convert the float, we can see the error message
Don't know how to convert parameter 2, and parameter 2 is the float, so we need to tell python what float is this
py code snippet start
# ...
mylib.my_func(i, ctypes.c_float(f))py code snippet end
code snippet start
> python3 main.py
i=3, f=0.500000
code snippet end