2012-01-19 14 views
19

Ho una classe C++. È composto da un file .ccp e un file .h. Compila (posso scrivere un metodo principale che lo utilizza con successo in C++). Come faccio a racchiudere questa classe con Cython per renderla disponibile in Python?Come faccio a racchiudere una classe C++ con Cython?

Ho letto i documenti e non seguo. Parlano della generazione del file cpp. Quando ho provato a seguire i documenti, il mio cpp già esistente viene spazzato via ...

Cosa devo fare per inserire il file pyx? Mi è stata detta la definizione della classe, ma quanto? Solo i metodi pubblici?

Ho bisogno di un file .pxd? Non capisco quando questo file è o non è richiesto.

Ho provato a porre queste domande nel canale IRC#python e non riesco a ottenere una risposta.

+0

Se la tua domanda ha avuto risposta, segna questa domanda come risposta. Altrimenti, specifica di nuovo la tua domanda. –

+0

@NiklasR La tua risposta ha coperto alcune delle mie aree di confusione ma non mi ha fatto arrivare fino in fondo. Ti ho dato un voto ma intendevo scrivere una risposta completa degli altri bit che ho elaborato quando ho un po 'di tempo libero. – Endophage

risposta

6

Quindi dopo un sacco di tentativi, tentativi ed errori, urlando e strappandomi i capelli, finalmente ho fatto in modo che funzionasse. Per prima cosa, ho dovuto riscrivere il mio C++ in C, che per me ha davvero coinvolto solo la conversione di tutte le mie variabili std::string in char* e di tenere traccia di alcune lunghezze.

Una volta terminato, avevo i file .h e .c. Volevo creare una singola funzione dal codice C disponibile in Python. Si scopre che Cython può compilare i tuoi file C nell'estensione per te e collegare tutte le librerie tutto in una volta, quindi a partire dalla mia configurazione.py, ha finito per assomigliare a questo:

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

ext_modules=[ 
    Extension("myext", 
    ["myext.pyx", "../stuff.c"], 
    libraries=["ssl", "crypto"] 
) 
] 

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

Come si può vedere, il secondo argomento per l'estensione elenca semplicemente tutti i file che devono essere compilati, Cython funziona come compilare loro a seconda della loro estensione, per quanto posso dire. L'array di librerie dice al compilatore di Cython ciò che deve essere collegato (in questo caso stavo avvolgendo alcune cose crittografiche che non riuscivo a imitare direttamente attraverso le librerie Python esistenti).

Per rendere effettivamente disponibile la mia funzione C nel file .pyx, si scrive un piccolo wrapper in .pxd. Il mio myext.pxd sembrava come di seguito:

cdef extern from "../stuff.h": 
    char* myfunc(char* arg1, char* arg2, char* arg3) 

Nella .pyx quindi si utilizza la dichiarazione cimport di importare questa funzione, che è quindi disponibile per l'uso, come se si trattasse di qualsiasi altra funzione Python:

cimport myext 

def my_python_func(arg1, arg2, arg3): 
    result = myext.myfunc(arg1, arg2, arg3) 
    return result 

Quando lo costruisci (almeno su Mac) ottieni un .so che puoi importare in python ed eseguire le funzioni da .pyx. Potrebbe esserci un modo migliore, più corretto, per far funzionare tutto questo, ma che deriva dall'esperienza e questo è stato il primo incontro che sono riuscito a risolvere. Sarei molto interessato a puntatori in cui potrei aver sbagliato.

Aggiornamento:

Dopo ulteriore utilizzo di Cython, ho scoperto che era super semplice da integrare con C++ troppo, una volta che sai quello che stai facendo. Rendere disponibile il string di C++ è semplice come from libcpp.string cimport string nella tua pyx/pyd. Dichiarare la classe C++ è analogamente semplice come:

cdef extern from "MyCPPClass.h": 
    cdef cppclass MyCPPClass: 
     int foo; 
     string bar; 

Certo bisogna ridichiarare fondamentalmente la definizione .h della classe in un formato Pythonic, ma questo è un piccolo prezzo da pagare per ottenere l'accesso alle funzioni già scritto in C++ .

+0

Per favore aiutatemi! Mi piacerebbe davvero usare Python 3.3 con Cython e C++, potresti avere qualche file di esempio? Ho installato Cython e Python più recenti. – PascalVKooten

+0

Non ho file di esempio per Python 3, tuttavia, assicurati di impostare correttamente i flag del compilatore http://wiki.cython.org/FAQ#WhatPythonversionsdoesCythonsupport.3F Fai una domanda qui su SO con altro problemi specifici che stai incontrando. Se lo colleghi qui nei commenti, posso dare un'occhiata. – Endophage

+0

Non sono sicuro se questo sarà particolarmente utile come esempio, ma ho scritto [questo Python 3.3 wrapper per una libreria C++] (https://github.com/wmayner/pyemd). – Will

3

Cython è principalmente per lo sviluppo C, per integrare C++ con Python, io suggerisco Boost.Python. La loro eccellente documentazione dovrebbe iniziare molto rapidamente.

+0

Oltre a Boost.Python, Shinboken (dal progetto PySide) e SIP (dal progetto PyQT) e credo che SWIG possa anche avvolgere oggetti C++. Potresti in teoria racchiudere il codice C++ con Cython, ma dovresti usare nomi storti e fare un sacco di cose a mano - non lo consiglierei io stesso. – synthesizerpatel

+0

Come ho * molto * i migliori saluti per Boost e la qualità di molte delle sue librerie, di solito è la mia prima scelta quando viene fornito un elenco di opzioni. Per quanto riguarda le opzioni che hai presentato: PySide è relativamente nuovo e per quanto mi riguarda, non dimostrato. SIP è sotto licenza GPL che potrebbe scoraggiare alcune persone. Ho avuto una spiacevole esperienza con SWIG, quindi non posso consigliarlo da solo. Come sempre, YMMV, ma secondo me difficilmente si può sbagliare con Boost. – spatz

+0

@spatz Mi dispiace ma non posso essere d'accordo su un'eccellente documentazione per Boost.Python, o anche su Boost in generale. Ho trovato la loro documentazione semplicemente fuorviante o mancante completamente nel passato. Un paio di mesi fa stavo lavorando con le loro librerie di rete/socket ed è stata un'enorme quantità di tentativi ed errori. – Endophage

14

Anche Cython è generalmente per l'uso con C, può generare C++ codice, anche. Durante la compilazione, aggiungi il flag --cplus.

Ora, la creazione di un wrapper per la classe è semplice e non molto diversa dall'involucro di una struttura. Differisce principalmente dal dichiarare lo extern, ma non c'è molta differenza.

Supponiamo di avere una classe MyCppClass in mycppclass.h.

cdef extern from "mycppclass.h": 
    cppclass MyCppClass: 
     int some_var 

     MyCppClass(int, char*) 
     void doStuff(void*) 
     char* getStuff(int) 

cdef class MyClass: 

    # the public-modifier will make the attribute public for cython, 
    # not for python. Maybe you need to access the internal C++ object from 
    # outside of the class. If not, you better declare it as private by just 
    # leaving out the `private` modifier. 
    # ---- EDIT ------ 
    # Sorry, this statement is wrong. The `private` modifier would make it available to Python, 
    # so the following line would cause an error es the Pointer to MyCppClass 
    # couldn't be converted to a Python object. 
    #>> cdef public MyCppClass* cobj 
    # correct is: 
    cdef MyCppClass* obj 

    def __init__(self, int some_var, char* some_string): 
     self.cobj = new MyCppClass(some_var, some_string) 
     if self.cobj == NULL: 
      raise MemoryError('Not enough memory.') 

    def __del__(self): 
     del self.cobj 

    property some_var: 
     def __get__(self): 
      return self.cobj.some_var 
     def __set__(self, int var): 
      self.cobj.some_var = var 

Nota che la parola chiave new è disponibile solo quando è impostato il flag --cplus, altrimenti utilizzare malloc da <stdlib.h> da externing esso.

Si noti inoltre che non è necessario il dereferenziamento del puntatore (->) per chiamare il metodo. Cython tiene traccia del tipo di oggetto e applica ciò che si adatta.

. I file .pxd servono per separare le dichiarazioni dall'implementazione o per evitare la collisione nello spazio dei nomi. Immagina che ti piacerebbe chiamarti Python-wrapper come la classe C++. Inserisci semplicemente nel file .pxd le dichiarazioni extern e cimport il file pxd nel file .pyx.

cimport my_pxd 
cdef my_pxd.SomeExternedType obj 

Si noti che non è possibile scrivere le implementazioni in un file .pxd.

+1

Se hai ulteriori domande, non ti dispiace chiedere. :) –

+0

Quindi il mio C++ utilizza la funzionalità di OpenSSL. Queste funzioni sono quelle che devo dichiarare nel mio pxd? – Endophage

+0

Inoltre, posso convertire il C++ a C, ma che non ha davvero aiutato a ottenere ogni ulteriore con il problema ... – Endophage

Problemi correlati