2015-02-06 123 views
6

我已經完全複製了Cython documentation for wrapping C++ classes中給出的示例代碼。我可以成功地建立和導入使用distutilscythonize()方法rect.so延伸,通過即:如何手動編譯使用C++的Cython代碼?

# distutils: language = c++ 
# distutils: sources = Rectangle.cpp 
  • setup.py文件,其中包含:

    1. rect.pyx頂部把下面的指令這個:

      from distutils.core import setup 
      from Cython.Build import cythonize 
      
      setup(
          name = "rectangleapp", 
          ext_modules = cythonize('*.pyx'), 
      ) 
      
    2. 打電話

      $ python setup.py build_ext --inplace 
      

    然而,當我包裝C代碼用Cython我經常發現它更方便手動命令行編譯單個擴展,即:

    1. 生成.c代碼使用命令行Cython編譯器

      $ cython foo.pyx 
      
    2. 使用手動編譯它:

      $ gcc -shared -fPIC -O3 -I /usr/lib/python2.7 -L /usr/lib/python2.7 \ 
           foo.c -lpython2.7 -o foo.so 
      

    我已經嘗試採用同樣的工藝打造的rect.so上面的例子:

    $ cython --cplus rect.pyx 
    $ g++ -shared -fPIC -O3 -I /usr/lib/python2.7 -L /usr/lib/python2.7 \ 
         rect.cpp -lpython2.7 -o rect.so 
    

    無論是用Cython和g ++編譯步驟似乎成功 - 我不明白任何命令行輸出,並在最後我有一個rect.so內置。然而,當我再嘗試導入模塊我得到一個錯誤undefined symbol

    In [1]: import rect 
    --------------------------------------------------------------------------- 
    ImportError        Traceback (most recent call last) 
    <ipython-input-1-ba16f97c2145> in <module>() 
    ----> 1 import rect 
    
    ImportError: ./rect.so: undefined symbol: _ZN6shapes9Rectangle9getLengthEv 
    

    什麼是手動編譯一個封裝C++類用Cython代碼正確的程序?

  • 回答

    6

    這裏的問題是,你說的地方,你會提供一個名爲Rectangle類的定義 - 在示例代碼中指出

    cdef extern from "Rectangle.h" namespace "shapes": 
        cdef cppclass Rectangle: 
         ... 
    

    然而,當你編譯你沒有提供的庫代碼爲Rectangle或包含它的庫,所以rect.so不知道在哪裏可以找到這個Rectangle類。

    要運行您的代碼,您必須先創建Rectangle對象文件。

    gcc -c Rectangle.cpp # creates a file called Rectangle.o 
    

    現在,您可以創建動態對抗鏈接庫,或靜態目標文件鏈接到rect.so。我會首先介紹靜態鏈接,因爲它最簡單。

    gcc -shared -fPIC -I /usr/include/python2.7 rect.cpp Rectangle.o -o rect.so 
    

    請注意,我沒有包含python庫。這是因爲你期望你的庫被python解釋器加載,所以當你的庫被加載時,python庫已經被加載。除了提供rect.cpp作爲來源,我還提供Rectangle.o。所以讓我們試着用你的模塊來運行一個程序。

    run.py

    import rect 
    print(rect.PyRectangle(0, 0, 1, 2).getLength()) 
    

    不幸的是,這將產生另一個錯誤:

    ImportError: /home/user/rectangle/rect.so undefined symbol: _ZTINSt8ios_base7failureE 
    

    這是因爲用Cython需要C++標準庫,但是Python有沒有裝好了。您可以通過添加C++標準庫所需的庫rect.so

    gcc -shared -fPIC -I/usr/include/python2.7 rect.cpp Rectangle.o -lstdc++ \ 
        -o rect.so 
    

    運行run.py再解決這個問題,所有應該工作。但是,rect.so的代碼比需要的大,尤其是如果您生成依賴於相同代碼的多個庫。你可以動態鏈接矩形代碼,也可以將它作爲一個庫。

    gcc -shared -fPIC Rectangle.o -o libRectangle.so 
    gcc -shared -fPIC -I/usr/include/python2.7 -L. rect.cpp -lRectangle -lstdc++ \ 
        -o rect.so 
    

    我們編譯矩形代碼到當前目錄中的共享庫,並提供-L.以使gcc知道如何尋找當前目錄和庫-lRectangle以使gcc知道如何尋找的矩形庫。最後,爲了能夠運行你的代碼,你必須告訴python Rectangle庫在哪裏。運行蟒蛇之前進入

    export LD_LIBRARY_PATH="/home/user/rectangle" # where libRectangle.so lives 
    

    您可以使用shell腳本,以確保你運行程序之前這樣做,每次,但它使事情混亂。最好只是堅持靜態鏈接矩形。