2013-05-28 47 views
8

我想爲我的C++項目提供Python接口。從技術上講,我決定使用Cython來封裝C++代碼。隨着時間的推移,整個項目將成爲Python擴展模塊,但起初這是高度實驗性的。逐漸地,C++類需要暴露給Python。使用Cython和C++進行項目組織

我的問題是如何最好地組織文件和構建配置,以便Cython生成的和人工編寫的C++代碼不會混合,並且Python擴展模塊與其他目標完全分離。

我想象一個像這樣的源文件的目錄結構,以及Cython的一些構建目錄。

Project/ 
    src/ 
     *.h 
     *.cpp 
    cython/ 
     Project.pyx 
     setup.py 
+0

你的C++項目可以在沒有Python的情況下使用嗎?創建並行ctypes,基於cffi的綁定(以支持更多不同交易的目標)提供相同的Python界面是否有意義? – jfs

+0

@ J.F.Sebastian該項目可以並且應該在沒有Python的情況下使用(現在)。我不明白你的第二個問題。 – clstaudt

+2

然後在邏輯上你有兩個項目:libproject(C++庫)和pyproject(允許使用Python中的libproject的Python模塊)。爲了便於使用(更容易從源代碼安裝並共同開發),您可以在pyproject中包含libproject。關於第二個問題:基於ctypes的綁定可能更容易在多個Python版本上部署。基於cffi的綁定可以用於更好的Pypy支持。例如,'pyinotify'既有基於ctypes的本地擴展,也有用C寫的本地擴展,'pyzmq'既有cffi也有基於Cython的實現。你是否需要它取決於你的項目。 – jfs

回答

5

基本上我有3個文件夾:

  1. CPROJECT,C++庫:產生libcproject.so共享對象
  2. CYPROJECT,該cythonized Python擴展:製造使用用Cython
  3. DEPENDENCIEScyproject.so,依賴關係:我複製兩個項目的外部需求

在1.我構建C++擴展(用gcc編譯 - -shared-fPIC編譯選項),其將暴露於Python和該CYPROJECT依賴於暴露的功能到Python。作爲後處理命令,生成的.so被複制到DEPENDENCIES/libcproject/(以及include文件)中。這樣,該庫當然也可以在純C++項目中獨立使用。

在2.我使用3子文件夾:

  • adapters:主要含有C++附加的類(通常從由libcproject.so提供的那些派生類)。這些通常是增強了特定於Cython要求的功能的類(例如,通過Py_XINCREFPy_DECREF,...)存儲給定類和參考計數管理的目標Python版本的PyObject * C版本(繼承自object) 。
  • pyext:這裏存放着所有的Cython手寫的.pyx文件。
  • setup:含setup.sh腳本(建立依賴路徑和調用python setup.py build_ext --inplace產生最終cyproject.so(添加到PYTHONPATH)和cyproject.pyx

那麼什麼是在setup子文件夾?

下面是setup.sh一個示例代碼:

export PYTHONPATH=$PYTHONPATH:../../../DEPENDENCIES/Cython-0.18 
export PATH=$PATH:../../../DEPENDENCIES/libcproject:../../../DEPENDENCIES/Cython-0.18/bin 

# Note the `../../../DEPENDENCIES/libcproject`... 

CC="gcc" \ 
CXX="g++" \ 
    python setup.py build_ext --inplace 

而這裏的setup.py一個例子(主要是爲了顯示附加adapters如何編譯):

import sys 
import os 
import shutil 

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

# Cleaning 
for root, dirs, files in os.walk(".", topdown=False): 
    for name in files: 
     if (name.startswith("cyproject") and not(name.endswith(".pyx"))): 
      os.remove(os.path.join(root, name)) 
    for name in dirs: 
     if (name == "build"): 
      shutil.rmtree(name) 

# Building 
setup(
    cmdclass = {'build_ext': build_ext}, 
    ext_modules = [ 
    Extension("cyproject", 
       sources=["cyproject.pyx", \ 
         "adapter/ALabSimulatorBase.cpp", \ 
         "adapter/ALabSimulatorTime.cpp", \ 
         "adapter/ALabNetBinding.cpp", \ 
         "adapter/AValueArg.cpp", \ 
         "adapter/ALabSiteSetsManager.cpp", \ 
         "adapter/ALabSite.cpp", \ 
         ], 
       libraries=["cproject"], 
       language="c++", 
       extra_compile_args=["-I../inc", "-I../../../DEPENDENCIES/python2.7/inc", "-I../../../DEPENDENCIES/gsl-1.8/include"], 
       extra_link_args=["-L../lib"] 
       extra_compile_args=["-fopenmp", "-O3"], 
       extra_link_args=[] 
      ) 
    ] 
)     

最後,主.pyx ,鏈接所有手寫的.pyx s的cython部分[cyproject.pyx]:

include "pyext/Utils.pyx" 
include "pyext/TCLAP.pyx" 
include "pyext/LabSimulatorBase.pyx" 
include "pyext/LabBinding.pyx" 
include "pyext/LabSimulatorTime.pyx" 
... 

注:通過用Cython生成的所有文件保留在此setup文件夾,從手寫的東西(adapterspyext)很好的分離,符合市場預期。

在3使用分離DEPENDENCIES文件夾允許保留的東西很好地分離(如果我將移動CYPROJECT - 和它的依賴 - 在其他一些環境)。

所有這些給你一個關於如何組織這樣的項目的概述(我希望有一個相關的)。

+0

這是一個相當具體的例子,但仍然是一個有用的例子。 – clstaudt

+0

但我認爲這將符合你的需求,因爲具體的部分是我使用共享的C++庫(你應該也應該擁有)以及適配器(我猜你很快就會需要它)。 –

+0

好的,隨着我的項目發展,我會提到你的例子。 – clstaudt