提问人:JamesTheAwesomeDude 提问时间:11/8/2023 更新时间:11/8/2023 访问量:21
重命名/重新声明 CFFI 的函数?
Renaming/re-declaring functions for CFFI?
问:
我有一个 C 库,我正在尝试为其创建(外行,API 模式)CFFI 绑定。C 库提供了每个函数的各种实现,但它们都添加了这个巨大的、令人讨厌的前缀。
例如,GreenSpam 子模块中 foo 函数的 AVX 优化实现被命名为 ;我将把它公开为 ,并在 -time 透明地选择 CPU 优化。THELIBRARY_GREENSPAM_OPTIMIZED_AVX_foo()
TheLibrary.Spam.GreenSpam.foo
import
我能够在 -time 确定每个模块的这个前缀,所以我想尝试尽可能地倾向于 DRY。我的代码目前看起来像这样,正如你所看到的,它与 DRY 一样可怕,并且没有提供任何简单或明显的途径来推进运行时算法的选择:ffibuilder.compile()
# TheLibrary/Spam/GreenSpam.py
from . import _greenspam_generic
from . import _greenspam_optimized_avx # TODO
__all__ = ['foo', 'oof']
def foo(bar):
"""GreenSpam foo"""
_out = _greenspam_generic.ffi.new(f"uint8_t[{len(bar):d}]")
_greenspam_generic.lib.THELIBRARY_GREENSPAM_GENERIC_foo(bar, len(bar), _out)
# void function never errors
return bytes(_out)
def oof(baz):
"""GreenSpam oof"""
_out = _greenspam_generic.ffi.new(f"uint8_t[{len(baz):d}]")
err = _greenspam_generic.lib.THELIBRARY_GREENSPAM_GENERIC_oof(baz, len(baz), _out)
if err:
raise RuntimeError("oof")
return bytes(_out)
# TheLibrary/Spam/PurpleSpam.py
from . import _purplespam_generic
from . import _purplespam_optimized_avx # TODO
__all__ = ['foo', 'oof']
def foo(bar):
"""PurpleSpam foo"""
_out = _purplespam_generic.ffi.new(f"uint8_t[{len(bar):d}]")
_purplespam_generic.lib.THELIBRARY_PURPLESPAM_GENERIC_foo(bar, len(bar), _out)
# void function never errors
return bytes(_out)
def oof(baz):
"""PurpleSpam oof"""
_out = _purplespam_generic.ffi.new(f"uint8_t[{len(baz):d}]")
err = _purplespam_generic.lib.THELIBRARY_PURPLESPAM_GENERIC_oof(baz, len(baz), _out)
if err:
raise RuntimeError("oof")
return bytes(_out)
我真的很想在运行时将这些 C 库函数名称扁平化,理想情况下公开别名,以使我的绑定代码更干净、更好,特别是使我能够添加这些导入时 CPU 选择,而不必再次乘法复制我的代码。ffi.compile()
(fr'\b{re.escape(prefix)}_(\w+)\b', r'\1')
我想尽可能密切地跟踪上游库,因此在 C 源代码上进行转换以删除这些前缀会很痛苦,我特别想询问这样做的替代方案。
但是,例如,我可以在 ,规范化别名添加到损坏的名称中,特别是如果可以以 DRY 为中心的方式完成。(我确实尝试过函数引用,但显然这些只是C++?ffibuilder.set_source()
我目前正在查看的特定库是 PQClean,但我希望我在将来的绑定中也会遇到这种情况,所以这是反对过多地深入转换 C 源代码的另一点,因为每次我遇到一个新库时,我都必须重新重复这项工作,并且每次该库重构其标头意大利面条时都可能再次重复。
答:
你当然可以玩一些 Python 级别的技巧。我会做这样的事情......(诚然,这使用了相当多的各种 Python hack)
# to make the builds "mylib_<suffix>.so"
for suffix in ['generic', 'optimized_avx']:
builder = FFIBuilder()
builder.set_source("mylib_" + suffix,
f"""
#include <mylib.h>
int foo_{suffix}(int x, int y);
void bar_{suffix}(void);
""")
builder.compile()
# minimal source code for mylib_generic.py
import _mylib_maker
globals().update(_mylib_maker.make("generic"))
# minimal source code for mylib_optimized_avx.py
import _mylib_maker
globals().update(_mylib_maker.make("optimized_avx"))
# actual Python-side wrappers defined in _mylib_maker.py
def make(suffix):
mod = __import__("mylib_" + suffix)
c_foo = getattr(mod, "foo_" + suffix)
c_bar = getattr(mod, "bar_" + suffix)
my_funcs = []
@my_funcs.append
def foo(x, y):
result = c_foo(x, y)
if result < 0: raise Exception
return result
@my_funcs.append
def bar():
return c_bar()
return {func.__name__: func for func in my_funcs}
评论