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)