Table of Contents
1 05 - Using conda to manage C++ libraries
2 Using conda to manage python libraries
2.1 Accessing the functions from python using CFFI
2.1.1 Running this in a threadpool
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
The cffi_practice folder shows how to install a simple python project using conda and setuptools
Demo
Show how to build and upload cffi_practice to my conda channel
The purpose of this module is to provide one function:
cffi_practice.get_paths()
That can be used to locate the library and header from cff_funs
The get_paths function is defined in this package in the init.py module.
Try:
conda install cff_practice -c phaustin
Then:
python -c 'import cffi_practice;print(cffi_practice.get_paths())'
Which should output something like:
{'libfile': '/Users/phil/mini36/lib/libcffi_funs.so', 'libdir': '/Users/phil/mini36/lib', 'includedir': '/Users/phil/mini36/include'}
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
[1]:
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}-")
found these functions in module: ['get_process_id', 'get_thread_id']
Here is the thread id in hex: -4fdf76a5-
here is the process ide in base 10: -16502-
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.
[2]:
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)
here are the pointers to hold the ids: [[<cdata 'char *' 0x7faa09f64970>], [<cdata 'char *' 0x7faa09f01f20>], [<cdata 'char *' 0x7faa09f2fb90>], [<cdata 'char *' 0x7faa09fbd840>], [<cdata 'char *' 0x7faa09f7aee0>], [<cdata 'char *' 0x7faa09f08c40>], [<cdata 'char *' 0x7faa09f08c90>], [<cdata 'char *' 0x7faa09f7b6a0>], [<cdata 'char *' 0x7faa09f7b6f0>], [<cdata 'char *' 0x7faa09f0ef90>]]
here are the thread ids
thread id: 1bb9329b
thread id: 72b7e656
thread id: 7f162e70
thread id: c9742b94
thread id: 9e63eefb
thread id: 6b8afbdc
thread id: c3945c37
thread id: 425f89f8
thread id: e612e1c6
thread id: f9e7dfdc
[ ]: