2008-09-28 11 views

Odpowiedz

109

Powinieneś rzucić okiem na Boost.Python. Oto krótkie wprowadzenie podjęte z ich strony internetowej:

The Boost Python Library is a framework for interfacing Python and C++. It allows you to quickly and seamlessly expose C++ classes functions and objects to Python, and vice-versa, using no special tools -- just your C++ compiler. It is designed to wrap C++ interfaces non-intrusively, so that you should not have to change the C++ code at all in order to wrap it, making Boost.Python ideal for exposing 3rd-party libraries to Python. The library's use of advanced metaprogramming techniques simplifies its syntax for users, so that wrapping code takes on the look of a kind of declarative interface definition language (IDL).

+0

Boost.Python jest jedną z bardziej przyjaznych dla użytkownika bibliotek w Boostu, ponieważ prosty interfejs wywołania funkcji jest dość prosty i zawiera szablon, który sam musisz napisać. To trochę bardziej skomplikowane, jeśli chcesz ujawnić obiektowy interfejs API. – jwfearn

41

Najszybszym sposobem, aby to zrobić jest użycie SWIG.

przykład z SWIG tutorial: Plik

/* File : example.c */ 
int fact(int n) { 
    if (n <= 1) return 1; 
    else return n*fact(n-1); 
} 

interfejsu:

/* example.i */ 
%module example 
%{ 
/* Put header files here or function declarations like below */ 
extern int fact(int n); 
%} 

extern int fact(int n); 

Budowa modułu Pythona Unix:

swig -python example.i 
gcc -fPIC -c example.c example_wrap.c -I/usr/local/include/python2.7 
gcc -shared example.o example_wrap.o -o _example.so 

Zastosowanie:

>>> import example 
>>> example.fact(5) 
120 

Pamiętaj, że musisz mieć program Python-Dev. Również w niektórych systemach pliki nagłówkowe pythona będą znajdować się w /usr/include/python2.7 w zależności od sposobu zainstalowania go.

z samouczka:

SWIG is a fairly complete C++ compiler with support for nearly every language feature. This includes preprocessing, pointers, classes, inheritance, and even C++ templates. SWIG can also be used to package structures and classes into proxy classes in the target language — exposing the underlying functionality in a very natural manner.

+0

Występowanie: nie może wykonać błędu pliku binarnego za pomocą tego samego kodu. Wydaje się być problemem na moim końcu, ale wszelkie wskazówki byłyby pomocne. – iDev

+0

@iDev brzmi jak osobne pytanie – Seanny123

13

nigdy nie używałem go, ale słyszałem dobre rzeczy o ctypes. Jeśli próbujesz użyć go w C++, unikaj mangerowania nazw przez extern "C". Dzięki za komentarz, Florian Bösch.

5

Jeden z oficjalnych dokumentów w języku Python zawiera szczegóły dotyczące extending Python using C/C++. Nawet bez użycia SWIG, jest to całkiem proste i działa doskonale w systemie Windows.

24

Zapoznaj się z pyrex lub Cython. Są to języki podobne do Pythona do łączenia się między C/C++ i Python.

+0

+1 dla Cython! Nie próbowałem cffi, więc nie mogę powiedzieć, co jest lepsze, ale miałem bardzo dobre doświadczenia z Cythonem - nadal piszesz kod Pythona, ale możesz użyć C w nim. Trochę trudno było mi skonfigurować proces kompilacji z Cythonem, który później wyjaśniłem w poście na blogu: http://martinsosic.com/development/2016/02/08/wrapping-c-library-as-python- module.html – Martinsos

443

Lubię ctypes dużo, swig zawsze tendencję, aby dać mi problems. Również ctypes ma tę zaletę, że nie musisz spełniać zależności od czasu kompilacji na pythonie, a twoje powiązanie będzie działało na każdym pytonie, który ma ctypy, nie tylko ten, z którego została skompilowana.

Załóżmy, że masz prostą klasę C++, z którą chcesz porozmawiać, w pliku o nazwie foo.cpp:

#include <iostream> 

class Foo{ 
    public: 
     void bar(){ 
      std::cout << "Hello" << std::endl; 
     } 
}; 

Od ctypes można mówić tylko do funkcji C, trzeba dostarczyć te deklarując je jako extern "C"

extern "C" { 
    Foo* Foo_new(){ return new Foo(); } 
    void Foo_bar(Foo* foo){ foo->bar(); } 
} 

Następny trzeba kompilować to udostępnionej biblioteki

g++ -c -fPIC foo.cpp -o foo.o 
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o 

I wreszcie trzeba napisać wrapper Pythona (np fooWrapper.py)

from ctypes import cdll 
lib = cdll.LoadLibrary('./libfoo.so') 

class Foo(object): 
    def __init__(self): 
     self.obj = lib.Foo_new() 

    def bar(self): 
     lib.Foo_bar(self.obj) 

Kiedy już, że można nazwać to jak

f = Foo() 
f.bar() #and you will see "Hello" on the screen 
+12

To jest właśnie to, co boost.python robi dla ciebie w jednym wywołaniu funkcji. –

+142

ctypes znajduje się w standardowej bibliotece Pythona, a swig i boost nie. Swig i boost polegają na modułach rozszerzających i dlatego są powiązane z mniejszymi wersjami Pythona, których nie są obiektami współużytkowanymi. budowanie owijania lub owijania może być uciążliwe, ctypes nie wymaga kompilacji. –

+10

boost polega na magii szablonów voodoo i całkowicie niestandardowym systemie kompilacji, ctypes opiera się na prostocie. ctypes jest dynamiczny, boost jest statyczny. ctypes może obsługiwać różne wersje bibliotek. doładowanie nie może. –

10

myślę cffi dla pytona może być opcja.

The goal is to call C code from Python. You should be able to do so without learning a 3rd language: every alternative requires you to learn their own language (Cython, SWIG) or API (ctypes). So we tried to assume that you know Python and C and minimize the extra bits of API that you need to learn.

http://cffi.readthedocs.org/en/release-0.7/

+2

Myślę, że to może wywoływać tylko c (nie C++), wciąż +1 (naprawdę lubię cffi). –

34

zacząłem moją podróż w Pythonie < -> C++ wiązania z tej strony, w celu łączenia typów danych na wysokim poziomie (wektory wielowymiarowy STL z list Python) :-)

Po wypróbowaniu rozwiązań opartych zarówno na ctypes jak i boost.python (i nie będąc inżynierem oprogramowania) odkryłem, że są one złożone, gdy wymagane jest wiązanie typów danych na wysokim poziomie, podczas gdy dla takich przypadków stwierdziłem, że jest ono o wiele prostsze.

W tym przykładzie użyto SWIG i został przetestowany w systemie Linux (ale SWIG jest dostępny i jest szeroko stosowany również w systemie Windows).

Celem jest udostępnienie funkcji języka C++ dla Pythona, który pobiera macierz w postaci wektora 2D STL i zwraca średnią z każdego wiersza (jako wektor 1D STL).

kod w C++ ("code.cpp") jest następujące:

#include <vector> 
#include "code.h" 

using namespace std; 

vector<double> average (vector< vector<double> > i_matrix) { 

    // Compute average of each row.. 
    vector <double> averages; 
    for (int r = 0; r < i_matrix.size(); r++){ 
    double rsum = 0.0; 
    double ncols= i_matrix[r].size(); 
    for (int c = 0; c< i_matrix[r].size(); c++){ 
     rsum += i_matrix[r][c]; 
    } 
    averages.push_back(rsum/ncols); 
    } 
    return averages; 
} 

odpowiednikiem nagłówka ("code.h") jest:

#ifndef _code 
#define _code 

#include <vector> 

std::vector<double> average (std::vector< std::vector<double> > i_matrix); 

#endif 

Najpierw kompilacji C++ Kod, aby utworzyć plik obiektu:

g++ -c -fPIC code.cpp 

następnie zdefiniować SWIG interface definition file ("code.i") dla naszej funkcji C++.

%module code 
%{ 
#include "code.h" 
%} 
%include "std_vector.i" 
namespace std { 

    /* On a side note, the names VecDouble and VecVecdouble can be changed, but the order of first the inner vector matters! */ 
    %template(VecDouble) vector<double>; 
    %template(VecVecdouble) vector< vector<double> >; 
} 

%include "code.h" 

Za pomocą SWIG generujemy kod źródłowy interfejsu C++ z pliku definicji interfejsu SWIG.

swig -c++ -python code.i 

W końcu kompilacji wygenerowany plik źródłowy C++ interfejs i połączyć wszystko razem, aby wygenerować wspólną bibliotekę, która jest bezpośrednio importable przez Python (dalej „_” sprawach)

g++ -c -fPIC code_wrap.cxx -I/usr/include/python2.7 -I/usr/lib/python2.7 
g++ -shared -Wl,-soname,_code.so -o _code.so code.o code_wrap.o 

Możemy teraz używać funkcja w skryptach w języku Python:

#!/usr/bin/env python 

import code 
a= [[3,5,7],[8,10,12]] 
print a 
b = code.average(a) 
print "Assignment done" 
print a 
print b 
+0

Prawdziwa implementacja przypadku, w której w wektorach stl kodu C++ są przekazywane jako odwołania nie będące const, a więc dostępne przez pythona jako parametry wyjściowe: http://lobianco.org/antonello/personal:portfolio:portopt – Antonello

3

Najpierw należy zdecydować, jaki jest Państwa cel. Oficjalna dokumentacja Pythona na extending and embedding the Python interpreter została wspomniana powyżej, mogę dodać dobrą overview of binary extensions. Przypadków użycia może być podzielony na 3 kategorie:

  • modułów przyspieszających: działać szybciej niż równoważne czysty kod Pythona przebiega CPython.
  • moduły opakowujące: aby odsłonić istniejące interfejsy C do kodu Pythona.
  • niski poziom dostępu do systemu: aby uzyskać dostęp do funkcji niższego poziomu środowiska wykonawczego CPython, systemu operacyjnego lub podstawowego sprzętu.

Aby dać szerszą perspektywę dla innych zainteresowanych, a ponieważ początkowe pytanie jest nieco niejasne ("do biblioteki C lub C++"), myślę, że ta informacja może być dla ciebie interesująca. Na powyższym linku można przeczytać na temat wad korzystania z rozszerzeń binarnych i ich alternatyw.

Oprócz innych proponowanych odpowiedzi, jeśli chcesz moduł akceleratora, możesz spróbować Numba. Działa "poprzez wygenerowanie zoptymalizowanego kodu maszynowego przy użyciu infrastruktury kompilatora LLVM w czasie importu, w czasie wykonywania lub statycznie (przy użyciu dołączonego narzędzia Pycc)".

4

Pytanie brzmi, jak wywołać funkcję C z Pythona, o ile dobrze zrozumiałem. Najlepszym rozwiązaniem są Ctypes (BTW przenośny dla wszystkich wariantów Pythona).

>>> from ctypes import * 
>>> libc = cdll.msvcrt 
>>> print libc.time(None) 
1438069008 
>>> printf = libc.printf 
>>> printf("Hello, %s\n", "World!") 
Hello, World! 
14 
>>> printf("%d bottles of beer\n", 42) 
42 bottles of beer 
19 

Aby zapoznać się ze szczegółowym poradnikiem, można zapoznać się z numerem my blog article.

+0

Warto zauważyć, że podczas ctypes są przenośne, twój kod wymaga biblioteki C specyficznej dla Windows. – Palec

4

Cython jest zdecydowanie najlepszym rozwiązaniem, chyba że przewiduje się pisanie wrapperów Java, w takim przypadku SWIG może być lepszym rozwiązaniem.

Polecam korzystanie z narzędzia wiersza poleceń runcython, dzięki czemu proces korzystania z Cython jest niezwykle prosty. Jeśli chcesz przekazać dane strukturalne do C++, spójrz na bibliotekę protobuf Google, jest to bardzo wygodne.

Oto minimalny przykłady zrobiłem, że używa obu narzędzi:

https://github.com/nicodjimenez/python2cpp

Nadzieję, że może być dobrym punktem wyjścia.

1

dla nowoczesnych C++, użytkowania cppyy: http://cppyy.readthedocs.io/en/latest/

Jest on oparty na Cling, interpreter C++ dla Clang/LLVM. Wiązania są wykonywane w czasie wykonywania i nie jest wymagany żaden dodatkowy język pośredni. Dzięki Clang obsługuje C++ 17.

zainstalować go przy użyciu pip:

$ pip install cppyy 

Dla małych projektów, wystarczy załadować odpowiednią bibliotekę i nagłówki, które Cię interesują Np. wziąć kod z przykładu ctypes jest ten wątek, ale podzielone na sekcje nagłówka i kod:

$ cat foo.h 
    class Foo { 
    public: 
     void bar(); 
    }; 

    $ cat foo.cpp 
    #include "foo.h" 
    #include <iostream> 

    void Foo::bar() { std::cout << "Hello" << std::endl; } 

skompilować:

$ g++ -c -fPIC foo.cpp -o foo.o 
    $ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o 

i używać go:

$ python 
    >>> import cppyy 
    >>> cppyy.include("foo.h") 
    >>> cppyy.load_library("foo") 
    >>> from cppyy.gbl import Foo 
    >>> f = Foo() 
    >>> f.bar() 
    Hello 
    >>> 

Duże projekty obsługiwane z automatycznym ładowaniem przygotowanych informacji odbicia i fragmentów cmake w celu ich utworzenia, dzięki czemu użytkownicy zainstalowanych pakietów mogą po prostu uruchomić:

$ python 
    >>> import cppyy 
    >>> f = cppyy.gbl.Foo() 
    >>> f.bar() 
    Hello 
    >>> 

Dzięki LLVM możliwe są zaawansowane funkcje, takie jak automatyczne tworzenie szablonów. Aby kontynuować przykład:

>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]() 
    >>> v.push_back(f) 
    >>> len(v) 
    1 
    >>> v[0].bar() 
    Hello 
    >>> 

Uwaga: Jestem autorem cppyy.

+0

Podczas gdy ten link może odpowiedzieć na pytanie, lepiej umieścić w nim istotne części odpowiedzi i podać link do odsyłacza. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni. - [Z recenzji] (/ opinia/niskiej jakości-posts/19014837) – duckmayr

+0

Dzięki za opinię. Zaktualizowałem odpowiedź instrukcjami instalacji i pokazałem, jak wykorzystać przykład przedstawiony wcześniej w tym wątku. –

Powiązane problemy