Cython包装使用另一个库的类

时间:2014-04-09 19:10:36

标签: python c++ cython

我已经获得了一些独立的C ++代码dbscan.cppdbscan.h。现在我试图将它包装在Cython中。我不确定如何正确地做到这一点,而且我对有关编译器和链接器以及库和makefile的知识有限而受阻。

此处PyDBSCAN_lib.pyx

# distutils: language = c++ 
# distutils: sources = dbscan.cpp

from libcpp.vector cimport vector
from libcpp.string cimport string
from libcpp cimport bool

cdef extern from "dbscan.h":
    cdef cppclass DBSCAN:
        #DBSCAN(int minPts, int eps) except +
        DBSCAN(int minPts) except +
        void start()
        void findNeighbors(int pid, vector[int]& neighbors)
        void readFile(string filename, bool lastColIsTrueCluster)
        void buildDistMatrix()
        void calcEps()
        void calcNumNeighbors()
        void initLabels()
        void writeFile(string filename)

cdef class PyDBSCAN:
    cdef DBSCAN *thisptr
    def __cinit__(self, int minPts):
        self.thisptr = new DBSCAN(minPts)
    def __dealloc__(self):
        del self.thisptr
    def start(self):
        self.thisptr.start()
    def findNeighbors(self, int pid, vector[int]& neighbors):
        self.thisptr.findNeighbors(pid, neighbors)
    def readFile(self, string filename, bool lastColIsTrueCluster):
        self.thisptr.readFile(filename, lastColIsTrueCluster)
    def buildDistMatrix(self):
        self.thisptr.buildDistMatrix()
    def calcEps(self):
        self.thisptr.calcEps()
    def calcNumNeighbors(self):
        self.thisptr.calcNumNeighbors()
    def initLabels(self):
        self.thisptr.initLabels()
    def writeFile(self, string filename):
        self.thisptr.writeFile(filename)

正如您所看到的,上半部分引用了我的c ++代码,而底部是一个包装类。

这里是setup.py,我理解它有点像makefile:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
#from Cython.Build import cythonize
import os

os.environ['CC'] = '/app/gcc/4.8.2/bin/g++ -std=c++11'
os.environ['CXX'] = '/app/gcc/4.8.2/bin/g++ -std=c++11'
os.environ['CPP'] = '/app/gcc/4.8.2/bin/g++ -std=c++11'
os.environ['CMAKE_CXX_COMPILER'] = '/app/gcc/4.8.2/bin/g++ -std=c++11'

modules = [Extension("PyDBSCAN_lib", 
                sources=["PyDBSCAN_lib.pyx"],
                include_dirs = [".", "/usr/local/elemental/0.81/HybridRelease/include"],
                libraries = ["mpi_cxx", "mpi", "m", "elemental"],
                library_dirs = ["/usr/local/lib", "/usr/lib", "/usr/local/elemental/0.81/HybridRelease/lib"],
                language = "c++")]

setup(ext_modules = modules, cmdclass = {"build_ext" : build_ext})  

我尝试生成PyDBSCAN_lib.so,以便我可以在任何常规python脚本中导入它。

我的问题是dbscan.cpp在元素库中使用了某些类型,而我无法在setup.py中找到正确的配置来指定它。目前它产生了这个:

/usr/bin/ld: /usr/local/elemental/0.81/HybridRelease/lib/libelemental.a(matrix.cpp.o): relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC
/usr/local/elemental/0.81/HybridRelease/lib/libelemental.a: could not read symbols: Bad value
collect2: error: ld returned 1 exit status

编辑:为了记录,这里是我用g ++编译它的方式

include /usr/local/elemental/0.81/HybridRelease/conf/elemvariables 

db: dbscan_main.cpp dbscan.cpp 

    ${CXX} ${ELEM_COMPILE_FLAGS} -fopenmp $^ -o $@ ${ELEM_LINK_FLAGS} ${ELEM_LIBS}

其中elemvariables包含各种编译选项,但-fPIC不在其中。

我很感激任何帮助。感谢。

2 个答案:

答案 0 :(得分:1)

您是否尝试过执行链接器ld错误建议的操作?您可以在setup.py中的Extension对象构造中将-fPIC标志传递给编译器:

Extension("PyDBSCAN_lib",
          # ...
          extra_compile_args=['-fPIC'],
          extra_link_args=['-fPIC']
          # ...
          )

不确定这应该是编译器还是链接器标志;我提到了两种可能性,以便你了解这两种可能性。

答案 1 :(得分:1)

您需要使用-fPIC编译器标志编译 elemental ,以便从共享对象(如Python扩展模块)中使用它。将正常的可执行文件链接到没有此要求;这是代码共享对象的代码之一。

Distutils应该自动在它编译的代码上使用-fPIC标志,因为它知道它正在构建一个共享对象。