Cython:调用 C 函数抛出 'undefined symbol'

Cython: calling C function throws 'undefined symbol'

我正在尝试将 LMDB C API 与 Cython 一起使用。

我想从头文件中导入以下定义:

typedef struct MDB_env MDB_env;
int  mdb_env_create(MDB_env **env);

所以我创建了一个 .pxd 文件:

cdef extern from 'lmdb.h':
    struct MDB_env:
        pass
    int  mdb_env_create(MDB_env **env)

我在 Cython 脚本中使用它:

cdef MDB_env *e
x = mdb_env_create(&e)

这段代码编译得很好,但是如果我 运行 它,我得到:

ImportError: /home/me/.cache/ipython/cython/_cython_magic_15705c11c6f56670efe6282cbabe4abc.cpython-36m-x86_64-linux-gnu.so: undefined symbol: mdb_env_create

这在 Cython .pyx + .pxd 设置和输入 IPython.

的原型中都会发生

如果我导入另一个符号,比如说一个常量,我就可以访问它。所以我似乎在看正确的头文件。

我没有发现我的语法和文档之间有任何差异,但我显然做错了什么。有人可以给我提示吗?

谢谢。

要用 IPythons-magic 编译它(如果你能在你的问题中明确提到这一点会很好)你必须提供 library-path (通过 -L-选项)和库名称(通过 -l-选项)要包装的 built c-library,另请参阅 documentation:

%%cython  -L=<path to your library> -l=<your_library>

您尝试打包的库不是 header-only 库。这意味着某些符号(例如 mdb_env_create)仅在 header 中声明而未定义。构建库时,可以在生成的工件中找到这些符号的定义,构建扩展时应将其提供给链接器。这些定义是程序运行时所需要的。

如果您不这样做,Linux 会发生以下情况:构建扩展(*.so 文件)时,链接器默认允许未定义的符号 - 所以这一步是 "successful" - 但失败只是被推迟了。当通过 import 加载扩展时,Python 在 ldopen 的帮助下加载相应的 *.so 并且在此步骤中加载程序检查所有符号的定义是否已知。但是我们没有提供 mdb_env_create 的定义,所以加载器失败并显示

undefined symbol: mdb_env_create

在 header-file 中定义的符号不同,例如枚举 MDB_FIRST&Co - 编译库不是必需的,因此可以加载扩展,因为没有未定义的符号。