Cython:共享对象中的未定义符号

Posted

技术标签:

【中文标题】Cython:共享对象中的未定义符号【英文标题】:Cython: undefined symbol in shared object 【发布时间】:2018-05-17 09:58:53 【问题描述】:

我在 python 项目中导入 c++ 代码,一切似乎都编译得很好但是当我导入我的 .pyx 时:

from AGCython import *
ImportError: /path/to/shared/object/AGCython.cpython-36m-x86_64-linux-gnu.so: undefined symbol: c_Stat_GetMeanAndVariance_double

在我的 AGCython.pyx 中有:

cdef extern void c_Stat_GetMeanAndVariance_double (double* array, int nSize, double* mean, double* var)

及其 python 包装器

def Stat_GetMeanAndVariance_double(np.ndarray[double, ndim=1, mode="c"] input not None):
    cdef int m #nSize
    m = input.shape[0]
    cdef double mean, var
    c_Stat_GetMeanAndVariance_double(&input[0], m, &mean, &var)
    return mean, var

这个cpp函数在AGc.cpp中定义:

#include "AGc.h"
void c_Stat_GetMeanAndVariance_double(const double *aData, const int nSize, double &mean, double &var)

    // Special case, small vector
    if (nSize<=1)
    
        var= 0;
        if (nSize)
            mean= *aData;
        else
            mean= 0;
        return;
    

    double s, ssqr;
    Stat_GetSums_double(aData, nSize, s, ssqr);

    mean= s/nSize;
    var= Stat_GetVariance(s, ssqr, nSize);
    return;

并且 AGc.h 包含:

void c_Stat_GetMeanAndVariance_double(const double *aData, const int nSize, double &mean, double &var);

我的编译脚本是这样的:

from distutils.core import setup
from Cython.Build import cythonize
from distutils.extension import Extension
import numpy


sourcefiles = ['AGCython.pyx', 'AGc.cpp']

extensions = [Extension("AGCython", sourcefiles)]

setup(
    ext_modules = cythonize(extensions, annotate=True)
)

这会导致这个 gcc 调用:

gcc -pthread -B /home/ludvig/anaconda3/compiler_compat -Wl,--sysroot=/ -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -I/home/ludvig/anaconda3/include/python3.6m -c AGc.cpp -o build/temp.linux-x86_64-3.6/AGc.o
cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++
g++ -pthread -shared -B /home/ludvig/anaconda3/compiler_compat -L/home/ludvig/anaconda3/lib -Wl,-rpath=/home/ludvig/anaconda3/lib -Wl,--no-as-needed -Wl,--sysroot=/ build/temp.linux-x86_64-3.6/AGCython.o build/temp.linux-x86_64-3.6/AGc.o -o /path/to/my/project/AGCython.cpython-36m-x86_64-linux-gnu.so

我不明白我在这里错过了什么,我认为警告不是阅读this问题的问题

【问题讨论】:

【参考方案1】:

您的问题是名称修改。但是,它不起作用有两个原因。

第一

默认情况下,AGCython.pyx 使用没有名称修饰的 C 语言,即它希望符号具有名称 c_Stat_GetMeanAndVariance_double

附加文件是*.cpp,因此 gcc 决定将其编译为 C++ 源代码,这意味着名称修改开始对应的符号名称变为 _Z32c_Stat_GetMeanAndVariance_doublePKdiRdS1_。因此寻找未损坏的名称,加载程序在运行时失败。

有不同的方法可以修复它,但如果您打算使用 c++,最简单的方法是将language='c++' 添加到您的设置中:

extensions = [Extension("AGCython", 
                        sourcefiles,
                        language='c++')]

第二

您将导出的函数声明为:

cdef extern void c_Stat_GetMeanAndVariance_double (...)

从cython翻译成

__PYX_EXTERN_C DL_IMPORT(void) c_Stat_GetMeanAndVariance_double(...);

__PYX_EXTERN_C 是一个定义:

#ifndef __PYX_EXTERN_C
  #ifdef __cplusplus
    #define __PYX_EXTERN_C extern "C"
  #else
    #define __PYX_EXTERN_C extern
  #endif
#endif

这意味着它关闭了 C++ 案例的名称修改。为避免这种情况,您需要像通常那样在标头中包含该函数:

cdef extern from "AGc.h":
    void c_Stat_GetMeanAndVariance_double(...)

【讨论】:

嗨!感谢您的简洁回答!我将它复制到我的编译脚本中,但我仍然遇到同样的问题......是否可以在编译的代码中找到该函数并查看它是否已被破坏? @L.E.重建一切——当安装脚本改变时通常不会发生这种情况 我删除了共享对象,重建但没有运气。然而;我注意到在 .pyx 中,我将 extern 函数中的 mean 和 var 定义为指针参数,而 c 函数需要 mean 和 var 作为 reference。我将其更改为一致并遇到不同的问题:c_Stat_GetMeanAndVariance_double(&amp;input[0], m, &amp;mean, &amp;var) ^ AGCython.pyx:497:58: Cannot assign type 'double *' to 'double' 我接受您的回答,因为它似乎对原始问题是正确的 @L.E.仅删除 *.so 是不够的。您还必须重建 pyx 文件。仅当 pyx 文件已更改时,此操作才会自动完成。 是的,我知道,它确实重建了; running build_ext building 'AGCython' extension gcc -pthread -B /home/ludvig/anaconda3/compiler_compat 等等......【参考方案2】:

最终改变了我的诀窍

cdef extern void c_Stat_GetMeanAndVariance_double (double* array, int nSize, double &mean, double &var)

进入:

cdef extern from "AGc.h":
    void c_Stat_GetMeanAndVariance_double(const double* aData, const int nSize, double &mean, double &var)

从this page in the cython docs看

为什么这行得通,而不是我不确定的其他方式,但是

【讨论】:

抱歉,我不知何故忽略了您声明函数的方式,因为通常它是通过“extern from”完成的。如果您有兴趣,请查看我的更新,为什么它在原始版本中不起作用。 太棒了!感谢您澄清“幕后”发生的事情 - 非常感谢

以上是关于Cython:共享对象中的未定义符号的主要内容,如果未能解决你的问题,请参考以下文章

erlang nif共享库上的未定义符号

静态库中的未定义符号链接到动态库

动态加载库的未定义符号“typeinfo”

XCode 5 - 架构 armv7 的未定义符号:

C ++ / Eclipse中的未定义符号错误[关闭]

Python.h 中的未定义符号