尝试使用Cython Extensions时未定义的符号

时间:2017-08-12 23:37:41

标签: python c++ linux cython ld

我确信我缺少一些简单的东西,但我一直盯着它看,我不会看到它。我已经阅读了问题和答案hereherehereherehere,而没有遇到我的用例的解决方案。我正在尝试整理一个简单的测试结构,用于玩智能指针和cython(虽然我甚至无法进入智能指针部分)。代码如下:

test_unique_ptr.h

struct TestStruct {
    int a;
    int b;
};

class TestClass
{
public:
    TestClass(void);
    ~TestClass(void);

    TestStruct myts;

    int getA() { return myts.a; }
    int getB() { return myts.b; }
};

test_unique_ptr.cpp

#include "test_unique_ptr.h"

TestClass::TestClass(void) {
    myts.a = 4;
    myts.b = 7;
}

这成功编译成libTestUPtr.so

test_u_ptr.pyx

from libcpp.memory cimport unique_ptr
from cython.operator cimport dereference as deref

cdef extern from "test_unique_ptr.h":
    ctypedef struct TestStruct:
        int a
        int b

    cdef cppclass TestClass:
        TestClass()
        TestStruct foo
        int getA()
        int getB()

cdef class TestClass1:
    cdef:
        TestClass tc

    def __cinit__(self):
        self.tc = TestClass()

    def getValue(self):
        print(self.tc.getA())

foo = TestClass1()
foo.getValue()

setup.py

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
import numpy

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = [
        Extension("test_u_ptr",
            language="c++",
            sources=["test_u_ptr.pyx"],
            include_dirs=["/home/share/data/code/python/test/include", numpy.get_include()],
            libraries = ["TestUPtr"],
            library_dirs=["/home/share/data/code/python/test/lib"],
            extra_compile_args=['-lstdc++','-std=c++11', '-v'],
            extra_link_args=['-lstdc++', '-v'],
            )
        ]
    )

这也成功编译成test_u_ptr.cpython-35m-x86_64-linux-gnu.so。现在是有趣的部分。

$ python3
>>> import test_u_ptr
ImportError: /home/share/data/code/python/test/test_u_ptr.cpython-35m-x86_64-linux-gnu.so: undefined symbol: _ZN9TestClassD1Ev

符号似乎在libTestUPtr.so中正确定义

$ nm -C libTestUPtr.so

0000000000201030 B __bss_start

0000000000201030 b completed.6973
                 w __cxa_finalize
0000000000000580 t deregister_tm_clones
00000000000005f0 t __do_global_dtors_aux
0000000000200e38 t __do_global_dtors_aux_fini_array_entry
0000000000201028 d __dso_handle
0000000000200e48 d _DYNAMIC
0000000000201030 D _edata
0000000000201038 B _end
0000000000000680 T _fini
0000000000000630 t frame_dummy
0000000000200e30 t __frame_dummy_init_array_entry
0000000000000700 r __FRAME_END__
0000000000201000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000000530 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000200e40 d __JCR_END__
0000000000200e40 d __JCR_LIST__
                 w _Jv_RegisterClasses
00000000000005b0 t register_tm_clones
0000000000201030 d __TMC_END__
0000000000000670 T TestClass::TestClass()
0000000000000670 T TestClass::TestClass()

但是,几乎所有内容都未在cython库中定义。

$ nm -C test_u_ptr.cpython-35m-x86_64-linux-gnu.so

0000000000204530 B __bss_start
0000000000204540 b completed.6973
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000001be0 t deregister_tm_clones
0000000000001c50 t __do_global_dtors_aux
0000000000203d40 t __do_global_dtors_aux_fini_array_entry
0000000000204180 d __dso_handle
0000000000203d50 d _DYNAMIC
0000000000204530 D _edata
00000000002046f8 B _end
0000000000003154 T _fini
0000000000001c90 t frame_dummy
0000000000203d38 t __frame_dummy_init_array_entry
00000000000035b8 r __FRAME_END__
0000000000204000 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
00000000000015e8 T _init
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000203d48 d __JCR_END__
0000000000203d48 d __JCR_LIST__
                 w _Jv_RegisterClasses
                 U PyBaseObject_Type
                 U PyBytes_FromStringAndSize
                 U PyCapsule_New
                 U PyCFunction_Type
                 U _Py_CheckRecursionLimit
                 U _Py_CheckRecursiveCall
                 U PyCode_New
                 U PyDict_GetItem
                 U PyDict_GetItemString
                 U PyDict_New
                 U PyDict_SetItem
                 U PyDict_SetItemString
                 U PyErr_Format
                 U PyErr_Occurred
                 U PyErr_SetString
                 U PyErr_WarnEx
                 U PyEval_EvalCodeEx
                 U PyEval_EvalFrameEx
                 U PyExc_ImportError
                 U PyExc_NameError
                 U PyExc_SystemError
                 U PyExc_TypeError
                 U PyFrame_New
                 U PyFunction_Type
                 U Py_GetVersion
                 U PyImport_AddModule
                 U PyImport_GetModuleDict
00000000000023d0 T PyInit_test_u_ptr
                 U PyLong_FromLong
                 U PyMem_Malloc
                 U PyMem_Realloc
                 U PyMethod_Type
                 U PyModule_Create2
                 U PyModule_GetDict
                 U _Py_NoneStruct
                 U PyObject_Call
                 U PyObject_CallFinalizerFromDealloc
                 U PyObject_GetAttr
                 U PyObject_SetAttrString
                 U PyOS_snprintf
                 U PyThreadState_Get
                 U PyTraceBack_Here
                 U PyTuple_New
                 U PyTuple_Pack
                 U PyType_Ready
                 U PyUnicode_Decode
                 U PyUnicode_FromFormat
                 U PyUnicode_FromString
                 U PyUnicode_FromStringAndSize
                 U PyUnicode_InternFromString
0000000000204560 B __pyx_module_is_main_test_u_ptr
0000000000001c10 t register_tm_clones
                 U __stack_chk_fail@@GLIBC_2.4
0000000000204530 d __TMC_END__
000000000000333b r __pyx_k_end
0000000000003337 r __pyx_k_foo
0000000000204650 b __pyx_print
0000000000003332 r __pyx_k_file
0000000000003329 r __pyx_k_main
0000000000003320 r __pyx_k_test
00000000002046a0 b __pyx_lineno
0000000000204690 b __pyx_clineno
000000000000331a r __pyx_k_print
0000000000204580 b __pyx_methods
0000000000204630 b __pyx_n_s_end
0000000000204620 b __pyx_n_s_foo
0000000000204680 b __pyx_filename
0000000000204628 b __pyx_n_s_file
0000000000204600 b __pyx_n_s_main
00000000002045d0 b __pyx_n_s_test
0000000000204320 d __pyx_moduledef
00000000002045f0 b __pyx_n_s_print
0000000000204660 b __pyx_code_cache
0000000000003311 r __pyx_k_getValue
00000000002041a0 d __pyx_string_tab
00000000002046b0 b __pyx_empty_bytes
00000000002046c0 b __pyx_empty_tuple
0000000000001e10 t __Pyx_AddTraceback(char const*, int, int, char const*)
0000000000003302 r __pyx_k_pyx_vtable
0000000000204610 b __pyx_n_s_getValue
00000000002046a8 b __pyx_empty_unicode
00000000000018e0 t __Pyx_PyObject_Call(_object*, _object*, _object*)     
00000000002045e0 b __pyx_n_s_pyx_vtable
00000000000019c1 t __Pyx_PyFunction_FastCallDict(_object*, _object**, int,     _object*) [clone .constprop.3]
00000000002043a0 d __pyx_type_10test_u_ptr_TestClass1
0000000000204640 b __pyx_ptype_10test_u_ptr_TestClass1
0000000000001d30 t __pyx_tp_new_10test_u_ptr_TestClass1(_typeobject*, _object*, _object*)
00000000002045c0 b __pyx_vtable_10test_u_ptr_TestClass1
00000000002045a0 b __pyx_methods_10test_u_ptr_TestClass1
0000000000204670 b __pyx_vtabptr_10test_u_ptr_TestClass1
0000000000001ce0 t __pyx_tp_dealloc_10test_u_ptr_TestClass1(_object*)
0000000000002230 t __pyx_f_10test_u_ptr_10TestClass1_getValue(__pyx_obj_10test_u_ptr_TestClass1*)
0000000000001cd0 t __pyx_f_10test_u_ptr_10TestClass1_printValue(__pyx_obj_10test_u_ptr_TestClass1*)
00000000002046d0 b __pyx_b
00000000002046e0 b __pyx_d
00000000002046f0 b __pyx_m
                 U TestClass::TestClass()
                 U TestClass::~TestClass()

两个库都是64位。

$ file libTestUPtr.so 
libTestUPtr.so: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=4c6eb8560a244ebd485d483f1c27004145ad1882, not stripped

$ file test_u_ptr.cpython-35m-x86_64-linux-gnu.so 
test_u_ptr.cpython-35m-x86_64-linux-gnu.so: ELF 64-bit LSB  shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=5a2daa515d4a22dd46484968e817cd3a3bcebe5a, not stripped

libTestUPtr.so位于LD_LIBRARY_PATH并且位于正确的位置

$ echo $LD_LIBRARY_PATH 
/home/share/data/code/python/test/lib

$ ls /home/share/data/code/python/test/lib/
libTestUPtr.so

我不知道发生了什么。任何见解将不胜感激。

gcc是4.8.4版。 Python是版本3.5.3。 Cython是版本0.25.2。

1 个答案:

答案 0 :(得分:2)

您的问题没有任何神秘感,我怀疑,添加--no-undefined已修复它。

错过的符号是班级TestClass的析构函数。您的共享库为TestClass提供了两个符号:但这两个符号都是构造函数(我不完全理解为什么有两个符号以及为什么两个符号具有相同的地址)并且它们都不是所需的析构函数。 / p>

此问题的根源在于您声明了析构函数,因此它不再由编译器默认实现,但您不在cpp文件中提供实现。

所以你的选择是:

  1. 从h文件中删除析构函数的声明,让编译器处理它。

  2. 在cpp文件中实现析构函数。

  3. 哪一个更好取决于背景。

    gcc为构造函数发出两个符号,两个用于析构函数的符号似乎是一个已知的错过优化,here is a great explanation

    地址是相同的,因为编译器将两种相同的方法别名:

    _ZN9TestClassC2Ev: //Constructor of the BaseObject-constructor
    .LFB3:
        ...
        ret
    .LFE3:
        ..
        .globl  _ZN9TestClassC1Ev
        .set    _ZN9TestClassC1Ev,_ZN9TestClassC2Ev //HERE WE GO, Complete Object constructor which we use normally