7. Calling C++/Fortran using ctypes¶
The cffi_funs folder shows how to set up a C++ file so that it compiled with cmake and also here
The folder contains these files
Demo
Show how to build a conda archive with:
conda build .
and then upload it to your Anaconda channel
Where it can be installed into a conda environment with:
conda install -c phaustin cffi_funs
7.1. Using conda to manage python libraries¶
The cffi_practice defines the function get_paths() with locates the libcffi_funs.so module so python can import it
Demo
Show how to build and upload cffi_practice to my conda channel
The get_paths function is defined in this package in the init.py module.
7.2. Accessing the functions from python using CFFI¶
The C foreign function interface provides a way to call the cffi_funs from python
Here is an example that exposes the get_thread_id and get_proces_id functions from the cffi_fun package
from cffi import FFI
from joblib import Parallel
from cffi_practice import get_paths
#
# locate the library
#
lib_so_file=get_paths()['libfile']
ffi=FFI()
ffi.cdef("""
void get_thread_id(char *thread_id);
void get_process_id(char *process_id);
""")
#
# open the library file
#
lib = ffi.dlopen(lib_so_file)
print('found these functions in module: ',dir(lib))
#
# create a 25 character C array to hold the ouput
#
arg_thread = ffi.new("char[]",25) #(C++)
#
# copy the bytes into arg_thread (C++)
#
lib.get_thread_id(arg_thread)
#
# get the bytes into a python byte object
#
out_thread=ffi.string(arg_thread) #C++ to python
#
# turn the bytes into a utf-8 string
#
str_out=out_thread.decode('utf-8') #python
#
# print it out
#
print(f"Here is the thread id in hex: -{str_out}-")
#
# repeat for the process
#
arg_process = ffi.new("char[]",25)
lib.get_process_id(arg_process)
out_process=ffi.string(arg_process)
str_out=out_process.decode('utf-8')
print(f"here is the process ide in base 10: -{str_out}-")
7.2.1. Running this in a threadpool¶
The following script uses joblib to create 10 jobs to call the cffi functions in parallel, returning the pointers to the character array and convertingthem to python strings.
nprocs=10
arg_list=[]
fun_list=[]
dict_list=[]
for i in range(nprocs):
fun_list.append(lib.get_thread_id)
result_var=ffi.new("char[]",25)
arg_list.append(result_var)
dict_list.append({})
ptr_list=[[ffi.cast("char*",item)] for item in arg_list]
jobs=list(zip(fun_list,ptr_list,dict_list))
print(f'here are the pointers to hold the ids: {ptr_list}\n')
with Parallel(n_jobs=nprocs,backend='threading') as parallel:
parallel(jobs)
print('here are the thread ids')
for item in ptr_list:
out_thread=ffi.string(item[0]).decode('utf-8')
print('thread id: ',out_thread)