Wywołujesz C / c++ z Pythona? [zamknięte]

zamknięte . To pytanie musi być bardziej skoncentrowane . Obecnie nie przyjmuje odpowiedzi.

chcesz poprawić to pytanie? Update the question so it edytując ten post.

Zamknięte 4 miesiące temu .

Popraw to pytanie

Jaki byłby najszybszy sposób na zbudowanie wiązania Pythona do biblioteki C lub c++?

(używam Windows, jeśli to ma znaczenie.)

 543
Author: Peter Mortensen, 2008-09-28

12 answers

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

Biblioteka Pythona Boost jest frameworkiem do interfejsów Pythona i C++. Pozwala szybko i bezproblemowo wystawiać klasy C++ funkcji i obiektów do Pythona i odwrotnie, nie używając specjalnych narzędzia - tylko kompilator C++. Przeznaczony jest do zawijania interfejsów C++ nieinwazyjne, tak aby nie trzeba było zmieniać kodu C++ w wszystko po to, aby zawiń go, robi Boost.Python idealny do ekspozycji Biblioteki innych firm do Pythona. Korzystanie z biblioteki z zaawansowanych techniki metaprogramowania upraszczają jego składnię dla użytkowników, dzięki czemu wrapping code przybiera wygląd pewnego rodzaju deklaratywnego interfejsu język definicji (IDL).

 182
Author: Ralph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 22:58:57

Moduł Ctypes jest częścią biblioteki standardowej i dlatego jest bardziej stabilny i szeroko dostępny niż swig , który zawsze sprawiał mi Problemy.

Z ctypes, musisz zaspokoić wszelkie zależności w czasie kompilacji od Pythona, a twoje Wiązanie będzie działać na każdym Pythonie, który ma ctypes, a nie tylko ten, z którym został skompilowany.

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

#include <iostream>

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

Od ctypes może rozmawiać tylko z funkcjami C, musisz podać te deklarujące je jako extern"C"

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

Następnie musisz skompilować to do biblioteki współdzielonej

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

I na koniec trzeba napisać wrapper Pythona (np. w 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ż to masz, możesz to nazwać jak

f = Foo()
f.bar() #and you will see "Hello" on the screen
 696
Author: Florian Bösch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-07 15:48:56

Najszybszym sposobem jest użycie SWIG .

Przykład ze SWIG tutorial :

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

Plik interfejsu:

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

extern int fact(int n);

Budowanie modułu Pythona na Uniksie:

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

Użycie:

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

Zauważ, że musisz mieć 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 instalacji.

Z tutoriala:

SWIG jest dość kompletnym kompilatorem C++ z obsługą prawie każda funkcja językowa. Obejmuje to wstępne przetwarzanie, wskaźniki, klasy, dziedziczenie, a nawet szablony C++. SWIG może być również używany do pakowania struktur i klas w klasy proxy w języku docelowym - ujawniając podstawową funkcjonalność w bardzo naturalny sposób.

 58
Author: Ben Hoffstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-14 17:39:50

Rozpocząłem swoją podróż w wiązaniu Pythona C++ od tej strony, w celu połączenia typów danych wysokiego poziomu (wielowymiarowych wektorów STL z listami Pythona): -)

Po wypróbowaniu rozwiązań opartych zarówno na ctyp jak i boost.python (i nie będąc inżynierem oprogramowania) uznałem je za złożone, gdy wymagane jest Wiązanie typów danych wysokiego poziomu, podczas gdy znalazłem SWIG znacznie prostsze w takich przypadkach.

Ten przykład wykorzystuje zatem SWIG, i został przetestowany w Linuksie (ale SWIG jest dostępny i jest szeroko stosowany również w Windows).

Celem jest udostępnienie Pythonowi funkcji C++, która przyjmuje macierz w postaci wektora 2D STL i Zwraca średnią z każdego wiersza (jako wektor 1D STL).

Kod w C++ ("kod.cpp") jest następująca:

#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;
}

Równoważny nagłówek ("code.h") to:

#ifndef _code
#define _code

#include <vector>

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

#endif

Najpierw kompilujemy kod C++, aby utworzyć plik obiektowy:

g++ -c -fPIC code.cpp

Następnie definiujemy plik definicji interfejsu SWIG ("Kod.i") dla naszych 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"

Używając SWIG, generujemy kod źródłowy interfejsu C++ z pliku definicji interfejsu SWIG..

swig -c++ -python code.i

W końcu kompilujemy wygenerowany plik źródłowy interfejsu C++ i łączymy wszystko razem, aby wygenerować współdzieloną bibliotekę, która jest bezpośrednio importowana przez Pythona ("_"ma znaczenie):

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ć funkcji w skryptach Pythona:

#!/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
 51
Author: Antonello,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-04 23:05:25

Istnieje również pybind11, który jest jak lekka wersja Boost.Python i kompatybilny ze wszystkimi nowoczesnymi kompilatorami C++:

Https://pybind11.readthedocs.io/en/latest/

 45
Author: Tom Wenseleers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-18 15:43:41

Dla nowoczesnego C++, użyj cppyy: http://cppyy.readthedocs.io/en/latest/

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

Zainstaluj go za pomocą pip:

    $ pip install cppyy

W przypadku małych projektów wystarczy załadować odpowiednią bibliotekę i nagłówki, które Cię interesują. Np. weź kod z ctypes przykład jest ten wątek, ale podziel w nagłówku i sekcje kodu:

    $ 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żyj 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 są obsługiwane przez automatyczne ładowanie przygotowanych informacji o odbiciach i fragmentów cmake do ich tworzenia, 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 automatyczna instancja szablonu. 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.

 25
Author: Wim Lavrijsen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-06 18:21:21

Myślę, że cffi dla Pythona może być opcją.

Celem jest wywołanie kodu C z Pythona. Powinieneś być w stanie to zrobić bez nauki trzeciego języka: każda alternatywa wymaga od Ciebie naucz się własnego języka (Cython, SWIG) lub API (ctypes). Więc staraliśmy się założyć, że znasz Pythona i C i zminimalizować dodatkowe bity API, którego musisz się nauczyć.

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

 14
Author: mrgloom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-05 10:39:23

Pytanie brzmi, jak wywołać funkcję C z Pythona, jeśli dobrze zrozumiałem. Następnie najlepiej są Ctypy (BTW przenośne we wszystkich wariantach 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 uzyskać szczegółowy przewodnik, zapoznaj się z moim artykułem na blogu.

 8
Author: Jadav Bheda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-28 08:08:19

Pybind11 minimal runnable przykład

Pybind11 był wcześniej wspominany w https://stackoverflow.com/a/38542539/895245 ale chciałbym podać tutaj konkretny przykład użycia i dalszą dyskusję na temat implementacji.

Ogólnie polecam pybind11, ponieważ jest naprawdę łatwy w użyciu: wystarczy dołączyć nagłówek, a następnie pybind11 używa magii szablonów, aby sprawdzić klasę C++, którą chcesz wystawić na działanie Pythona i robi to przejrzyście.

Minusem tego szablonu jest to, że spowalnia kompilację natychmiast dodając kilka sekund do dowolnego pliku, który używa pybind11, patrz na przykład dochodzenie przeprowadzone w tej sprawie . PyTorch zgadza się. Wniosek dotyczący rozwiązania tego problemu został złożony pod adresem: https://github.com/pybind/pybind11/pull/2445

Oto minimalny przykład, aby dać ci poczucie, jak niesamowite pybind11 jest:

Class_test.cpp

#include <string>

#include <pybind11/pybind11.h>

struct ClassTest {
    ClassTest(const std::string &name) : name(name) { }
    void setName(const std::string &name_) { name = name_; }
    const std::string &getName() const { return name; }
    std::string name;
};

namespace py = pybind11;

PYBIND11_PLUGIN(class_test) {
    py::module m("my_module", "pybind11 example plugin");
    py::class_<ClassTest>(m, "ClassTest")
        .def(py::init<const std::string &>())
        .def("setName", &ClassTest::setName)
        .def("getName", &ClassTest::getName)
        .def_readwrite("name", &ClassTest::name);
    return m.ptr();
}

Class_test_main.py

#!/usr/bin/env python3

import class_test

my_class_test = class_test.ClassTest("abc");
print(my_class_test.getName())
my_class_test.setName("012")
print(my_class_test.getName())
assert(my_class_test.getName() == my_class_test.name)

Skompiluj i uruchom:

#!/usr/bin/env bash
set -eux
g++ `python3-config --cflags` -shared -std=c++11 -fPIC class_test.cpp \
  -o class_test`python3-config --extension-suffix` `python3-config --libs`
./class_test_main.py

Ten przykład pokazuje, w jaki sposób pybind11 pozwala bez wysiłku wystawić klasę ClassTest C++ na Pythona! Kompilacja tworzy plik o nazwie class_test.cpython-36m-x86_64-linux-gnu.so, który class_test_main.py automatycznie pobiera jako punkt definicji dla class_test natywnie zdefiniowanego modułu.

Być może uświadomienie sobie, jak niesamowite jest to tylko zanurza się, jeśli próbujesz zrobić to samo ręcznie z natywnym API Pythona, zobacz na przykład ten przykład robienia tego, który ma około 10x więcej kodu: https://github.com/cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c na tym przykładzie możesz zobaczyć, jak kod C musi boleśnie i jawnie definiować klasę Pythona bit po bit ze wszystkimi informacjami, które zawiera (członkowie, metody, dalsze metadane...). Zobacz też:

Pybind11 twierdzi, że jest podobny do Boost.Python, który został wymieniony na https://stackoverflow.com/a/145436/895245 jednak bardziej minimalistyczny, ponieważ jest uwolniony od nadęcia bycia wewnątrz projektu Boost:]}

Pybind11 jest lekką biblioteką tylko nagłówkową, która eksponuje typy C++ w Pythonie i odwrotnie, głównie w celu tworzenia powiązań Pythona z istniejącym kodem C++. Jego cele i składnia są podobne do doskonałego Boost.Biblioteka Pythona autorstwa Davida Abrahamsa: minimalizacja kodu boilerplate w tradycyjnych modułach rozszerzeń poprzez wnioskowanie informacji o typie za pomocą introspekcja w czasie kompilacji.

Główny problem z Boost.Python-a powodem stworzenia tak podobnego projektu-jest Boost. Boost jest ogromnie dużym i złożonym zestawem bibliotek narzędzi, które działają z prawie każdym istniejącym kompilatorem C++. Ta kompatybilność ma swój koszt: tajemne sztuczki szablonów i obejścia są niezbędne do obsługi najstarszych i najbardziej błędnych wzorów kompilatorów. Teraz, gdy Kompilatory kompatybilne z C++11 są powszechnie dostępne, ta ciężka maszyna stała się nadmiernie duża i niepotrzebna zależność.

Pomyśl o tej bibliotece jako małej samodzielnej wersji Boost.Pythona ze wszystkim, co nie jest istotne dla generowania wiązań. Bez komentarzy, Podstawowe pliki nagłówkowe wymagają tylko ~4k linii kodu i zależą od Pythona (2.7 lub 3.x, lub PyPy2.7 >= 5.7) oraz biblioteki standardowej C++. Ta kompaktowa implementacja była możliwa dzięki nowym funkcjom języka C++11 (w szczególności: krotki, funkcje lambda i różne szablony). Od momentu powstania, Biblioteka ta rozrosła się poza Boost.Python na wiele sposobów, co prowadzi do znacznie prostszego wiązania kodu w wielu typowych sytuacjach.

Pybind11 jest również jedyną nie natywną alternatywą, która jest dostępna w aktualnej dokumentacji wiążącej Microsoft Python C pod adresem: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in-visual-studio?view=vs-2019 ( archiwum ).

Testowane na Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0.

 7
Author: Ciro Santilli TRUMP BAN IS BAD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-09-14 15:16:44

Cython jest zdecydowanie najlepszym rozwiązaniem, chyba że przewidujesz pisanie wrapperów Javy, w którym to przypadku preferowany może być SWIG.

Polecam korzystanie z narzędzia wiersza poleceń runcython, sprawia, że proces korzystania z Cythona jest niezwykle łatwy. Jeśli chcesz przekazać dane strukturyzowane do C++, spójrz na bibliotekę protobuf Google, jest to bardzo wygodne.

Oto Minimalne przykłady, które zrobiłem, że używa obu narzędzi:

Https://github.com/nicodjimenez/python2cpp

Mam nadzieję, że może to być użyteczny punkt wyjścia.

 6
Author: nicodjimenez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-29 17:27:16

Najpierw powinieneś zdecydować, jaki jest twój konkretny cel. Oficjalna dokumentacja Pythona dotycząca rozszerzania i osadzania interpretera Pythona została wspomniana powyżej, Mogę dodać dobry Przegląd binarnych rozszerzeń. Przypadki użycia można podzielić na 3 kategorie:

  • Moduły akceleratora : aby działać szybciej niż odpowiednik czystego kodu Pythona, uruchamia się w CPython.
  • wrapper modules : Aby udostępnić istniejące interfejsy C Pythonowi kod.
  • Low level system access: dostęp do funkcji niższego poziomu środowiska uruchomieniowego CPython, systemu operacyjnego lub podstawowego sprzętu.

Aby dać szerszą perspektywę innym zainteresowanym i ponieważ Twoje 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żesz przeczytać o wadach używania rozszerzeń binarnych i ich alternatywach.

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

 5
Author: Yaroslav Nikitenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-15 11:17:09

Uwielbiam cppyy, dzięki temu bardzo łatwo rozszerzyć Pythona O kod C++, znacznie zwiększając wydajność w razie potrzeby.

Jest potężny i szczerze mówiąc bardzo prosty w użyciu,

Oto przykład jak można utworzyć tablicę numpy i przekazać ją do funkcji klasy w C++.

Cppyy_test.py

import cppyy
import numpy as np
cppyy.include('Buffer.h')


s = cppyy.gbl.Buffer()
numpy_array = np.empty(32000, np.float64)
s.get_numpy_array(numpy_array.data, numpy_array.size)
print(numpy_array[:20])

Bufor.h

struct Buffer {
  void get_numpy_array(double *ad, int size) {
    for( long i=0; i < size; i++)
        ad[i]=i;
  }
};

Możesz również bardzo łatwo utworzyć moduł Pythona (za pomocą CMake), w ten sposób unikniesz przekompilowania C++ koduj cały czas.

 5
Author: Garfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-09 21:58:30