GSL Python 化教程#
本教程介绍了 pythonizations 及其如何解决底层问题的使用方式。
设定:想象您想使用 numpy
,但被提供了一个基于 GNU 科学库(GNU Scientific Library,简称 GSL)的 C 或 C++ 库。如何让它们友好地协同工作?
GSL 是用 C 编写的,但结构非常严谨:它有一致的命名约定,为其主要结构提供分配器/解除分配器,并且具有明确的所有权规则。由于这种结构,可以根据反射信息编写 pythonizations,最终变得非常简单,因此易于维护。
import cppyy
/media/pc/data/lxw/envs/anaconda3x/envs/xxx/bin/python3.12: not an ELF file.
为了简化,我们将使用 gsl_blas_dgemm
作为“基于GSL的C/C++库”的替代。为了让我们的生活更轻松,我们将把绑定到 GSL 和 GSLBLAS 的内容包装成单个反射字典。对于简单项目来说,这有些过头了,但如果我们想访问所有的 GSL(而不是单独的 GSL 和 GSLBLAS),这种方法是可行的。
# first, pull in all headers from the GSL installation directory (/usr/include on my system).
import glob, os
GSL_HOME = '/usr/include'
gsl_headers = [os.path.relpath(x, GSL_HOME) for x in glob.glob(GSL_HOME+'/gsl/*.h')]
接下来,我们编写一个选择文件,从 GSL 中提取所有有趣的部分。由于适当的命名约定,这个文件可以保持简单(如果 GSL 是一个 C++ 库,并且位于一个命名空间中,它可能会更简单)。然后我们运行 genreflex
命令生成字典文件并编译它,链接到 GSL 和 GSLBLAS。最后,我们准备将字典反射文件加载到 cppyy
中。
!mkdir -p .temp
%%file .temp/gsl_selection.xml
<lcgdict>
<struct pattern="gsl_*" />
<function pattern="gsl_*" />
<enum pattern="GSL*" />
<enum pattern="CBLAS*" />
</lcgdict>
Overwriting .temp/gsl_selection.xml
# conventional name for generated output
rfldct = 'GSLDict'
if not os.path.exists(f'{rfldct}_rflx.cpp'):
import subprocess, sys
# generate the reflection dictionary
try:
subprocess.check_output(
['genreflex', # utility installed by pip when installing cppyy
'-s', '.temp/gsl_selection.xml', # selection file (see above)
'-o', f'.temp/{rfldct}_rflx.cpp', # intermediate output file
'-I'+GSL_HOME]+ # include search path for GSL headers
gsl_headers) # headers themselves
except subprocess.CalledProcessError as e:
print(f"genreflex failed ({e.returncode:d}): {e.output}")
else:
print("genreflex done")
genreflex failed (1): b''
Error: No valid header was provided!
if not os.path.exists('%s.so' % rfldct):
# get command line arguments for compiler from cling
try:
clingflags = subprocess.check_output(
['cling-config', # utility installed by pip when installing cppyy
'--cppflags'])
except subprocess.CalledProcessError as e:
print('cling-config failed (%d):' % e.returncode, e.output)
raise
else:
print('cling-config done')
# compile generated file
try:
subprocess.check_output(
['g++', # C++ compiler
'-fPIC', # require position independent code
'-shared', # generate shared library
'-o', '%s.so'%rfldct, # output file
'-I'+GSL_HOME, # include search path for GSL headers
'%s_rflx.cpp'%rfldct]+ # intermediate file to compile
clingflags.split()+ # extra flags provided by cling
['-lgsl', '-lgslcblas']) # link in GSL and GSLBLAS
except subprocess.CalledProcessError as e:
print('compilation failed (%d):' % e.returncode, e.output)
else:
print('compilation done')
cling-config done
compilation failed (1): b''
cc1plus: fatal error: GSLDict_rflx.cpp: No such file or directory
compilation terminated.
# load the generated dictionary
cppyy.load_reflection_info(rfldct)
---------------------------------------------------------------------------
RuntimeError Traceback (most recent call last)
Cell In[7], line 2
1 # load the generated dictionary
----> 2 cppyy.load_reflection_info(rfldct)
File /media/pc/data/lxw/envs/anaconda3x/envs/xxx/lib/python3.12/site-packages/cppyy/_cpython_cppyy.py:164, in load_reflection_info(name)
162 sc = gbl.gSystem.Load(name)
163 if sc == -1:
--> 164 raise RuntimeError("Unable to load reflection library "+name)
RuntimeError: Unable to load reflection library GSLDict