2012-01-19 16 views
19

Mam klasę C++. Składa się z jednego pliku .ccp i jednego pliku .h. Kompiluje (mogę napisać główną metodę, która z powodzeniem używa go w C++). Jak zawinąć tę klasę w Cython, aby była dostępna w Pythonie?Jak zawinąć klasę C++ za pomocą Cythona?

Czytałem dokumenty i nie stosuję się do nich. Mówią o generowaniu pliku cpp. Kiedy próbowałem podążać za dokumentami, mój już istniejący cpp zostaje zdmuchnięty ...

Co mam zamiar umieścić w pliku pyx? Powiedziano mi definicji klasy, ale ile z tego? Tylko publiczne metody?

Czy potrzebny jest plik .pxd? Nie rozumiem, kiedy ten plik jest lub nie jest wymagany.

Próbowałem już zadawać te pytania na kanale IRC#python i nie mogę uzyskać odpowiedzi.

+0

Jeśli odpowiedź na Twoje pytanie została udzielona, ​​zaznacz to pytanie jako odpowiedź. W przeciwnym razie proszę podać pytanie jeszcze raz. –

+0

@NiklasR Twoja odpowiedź obejmowała niektóre z moich obszarów zamieszania, ale nie doprowadziła mnie do końca. Podjąłem głosowanie, ale zamierzałem napisać pełną odpowiedź na inne kawałki, które wymyśliłem, gdy mam trochę czasu do stracenia. – Endophage

Odpowiedz

6

Po wielu szturchańcach, próbach i błędach, krzyczeniu i wydzieraniu włosów, w końcu udało mi się to zrobić. Najpierw jednak musiałem ponownie napisać moje C++ do C, co dla mnie naprawdę wymagało konwersji wszystkich zmiennych std::string na char* i śledzenia niektórych długości.

Kiedy skończyłem, miałem pliki .h i .c. Chciałem utworzyć jedną funkcję z kodu C dostępnego w Pythonie. Okazuje się, że Cython może skompilować twoje pliki C do rozszerzenia dla ciebie i połączyć wszystkie biblioteki za jednym razem, więc zaczynając od mojej instalacji.py, skończyło się patrząc tak:

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 
) 

Jak widać, drugi argument do przedłużenia po prostu listę wszystkich plików, które muszą być kompilowane, Cython odrabia jak skompilować je w zależności od ich rozszerzenia pliku o ile mogę powiedzieć. Tablica bibliotek informuje kompilator Cythona, co musi być połączone (w tym przypadku owijałem niektóre elementy kryptograficzne, których nie mogłem udawać bezpośrednio przez istniejące biblioteki Pythona).

Aby rzeczywiście udostępnić moją funkcję C w pliku .pyx, należy napisać małe opakowanie w pliku .pxd. Moja myext.pxd wyglądał jak poniżej:

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

W .pyx następnie użyć deklarację cimport importować tę funkcję, która jest wtedy gotowy do użycia, jakby to była jakakolwiek inna funkcja Python:

cimport myext 

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

Podczas kompilacji (na Macie przynajmniej) otrzymasz plik .so, który możesz zaimportować do Pythona i uruchomić funkcje z .pyx. Może być lepszy, bardziej poprawny sposób, aby wszystko działało, ale pochodzi z doświadczenia i to było pierwsze spotkanie, które udało mi się wypracować. Byłbym bardzo zainteresowany wskazówkami, w których mógłbym pójść źle.

Aktualizacja:

Po dalszego stosowania Cython, uważam, że to było bardzo proste, aby zintegrować go z C++ też, gdy wiesz, co robisz. Udostępnienie dostępnego w C++ string jest tak proste jak from libcpp.string cimport string w twoim pyx/pyd. Deklarując klasę C++ jest równie łatwe, jak:

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

Pewnie trzeba zasadzie redeclare się .h definicję klasy w formacie pythonic, ale to niewielka cena za uzyskanie dostępu do już napisanych funkcji C++ .

+0

Proszę, pomóżcie mi! Naprawdę chciałbym użyć Pythona 3.3 z Cython i C++, czy może masz jakieś przykładowe pliki? Mam zainstalowane najnowsze Cython i Python. – PascalVKooten

+0

Nie mam żadnych przykładowych plików dla Pythona 3, jednak upewnij się, że ustawiłeś poprawnie flagi kompilatora http://wiki.cython.org/FAQ#WhatPythonversionsdoesCythonsupport.3F Zadaj pytanie tutaj na temat SO z więcej konkretne problemy, które napotykasz. Jeśli połączysz go tutaj w komentarzach, mogę rzucić okiem. – Endophage

+0

Nie jestem pewien, czy będzie to szczególnie pomocne jako przykład, ale napisałem [to opakowanie Pythona 3.3 dla biblioteki C++] (https://github.com/wmayner/pyemd). – Will

3

Cython przeznaczony jest głównie do programowania w języku C, do integracji języka C++ z Pythonem Polecam Boost.Python. Ich doskonała dokumentacja powinna dość szybko zacząć pracę.

+0

Oprócz Boost.Python, Shinboken (z projektu PySide) i SIP (z projektu PyQT) i wierzę, że SWIG może również owijać obiekty C++. Mógłbyś teoretycznie zawinąć kod C++ za pomocą Cythona, ale musiałbyś użyć zniekształconych nazw i zrobić wiele rzeczy ręcznie - sam bym tego nie polecił. – synthesizerpatel

+0

Ponieważ mam * bardzo * wysokie uznanie za Boost i jakość wielu jego bibliotek, jest to zazwyczaj mój pierwszy wybór, gdy podano listę opcji. Co do przedstawionych przez ciebie opcji: PySide jest stosunkowo nowy i, o ile mi wiadomo, niepotwierdzony. SIP jest objęty licencją GPL, co może zniechęcić niektórych ludzi. Miałem pewne nieprzyjemne doświadczenia z SWIG, więc nie mogę tego polecić osobiście. Jak zawsze, YMMV, ale moim zdaniem nie można pomylić się z Boost. – spatz

+0

@spatz Przykro mi, ale nie mogę zgodzić się na doskonałą dokumentację dla Boost.Python, a nawet Boost w ogóle. Zauważyłem, że ich dokumentacja po prostu wprowadzała w błąd lub całkowicie zniknęła w przeszłości. Kilka miesięcy temu pracowałem z ich bibliotekami sieciowymi i był to ogromny okres prób i błędów. – Endophage

14

Nawet Cython jest ogólnie do użytku z C, może również wygenerować kod C++. Podczas kompilacji dodajesz flagę --cplus.

Teraz tworzenie wrapperów dla klasy jest proste i niewiele różni się od zawijania struktury. Różni się on głównie od zadeklarowania extern, ale to nie jest wielka różnica.

Załóżmy, że masz klasę MyCppClass w 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 

Zauważ, że słowo kluczowe new jest dostępna tylko wtedy, gdy jest ustawiona flaga --cplus, inaczej używać malloc z <stdlib.h> przez externing go.

Należy również pamiętać, że nie trzeba usuwać wskaźnika (->), aby wywołać metodę. Cython śledzi typ obiektu i stosuje to, co pasuje. Pliki

.pxd służą do oddzielania deklaracji od implementacji lub do unikania kolizji nazw. Wyobraź sobie, że chcesz nadać nazwę opakowaniu Pythona, takiemu jak klasa C++. Po prostu umieść w pliku .pxd deklaracje extern i cimport plik pxd w .pyx.

cimport my_pxd 
cdef my_pxd.SomeExternedType obj 

Uwaga: nie można pisać implementacji w pliku .pxd.

+1

Jeśli masz dodatkowe pytania, nie przejmuj się pytaniem. :) –

+0

Tak więc moje C++ wykorzystuje funkcjonalność OpenSSL. Czy te funkcje muszę zadeklarować w moim pxd? – Endophage

+0

Mogę też przekonwertować C++ na C, ale to nie pomogło mi w rozwiązaniu problemu ... – Endophage

Powiązane problemy