Wywoływanie C / C ++ z Python?

521

Jaki byłby najszybszy sposób na zbudowanie powiązania Pythona z biblioteką C lub C ++?

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

buta
źródło

Odpowiedzi:

170

Powinieneś spojrzeć na Boost.Python . Oto krótkie wprowadzenie z ich strony internetowej:

Biblioteka Boost Python jest strukturą dla interfejsów Python i C ++. Pozwala szybko i bezproblemowo udostępniać funkcje i obiekty klas C ++ w Pythonie i odwrotnie, bez użycia specjalnych narzędzi - tylko kompilatora C ++. Został zaprojektowany do nieinwazyjnego pakowania interfejsów C ++, dzięki czemu nie trzeba wcale zmieniać kodu C ++, aby go zawinąć, dzięki czemu Boost.Python jest idealny do udostępniania bibliotek innych firm w Pythonie. Zastosowanie przez bibliotekę zaawansowanych technik metaprogramowania upraszcza składnię dla użytkowników, dzięki czemu zawijanie kodu przypomina wygląd deklaratywnego języka definicji interfejsu (IDL).

Ralph
źródło
Boost.Python jest jedną z bardziej przyjaznych dla użytkownika bibliotek w Boost, dla prostego interfejsu API wywoływania funkcji jest dość prosty i zapewnia szablon, który musisz napisać sam. Jest to nieco bardziej skomplikowane, jeśli chcesz udostępnić obiektowy interfejs API.
jwfearn
15
Boost.Python to najgorsza rzecz, jaką można sobie wyobrazić. W przypadku każdej nowej maszyny i każdej aktualizacji wiąże się to z problemami z łączeniem.
młynarz
14
Prawie 11 lat później czas na kontemplację jakości tej odpowiedzi?
J Evans
4
Czy to wciąż najlepsze podejście do interfejsu python i c ++?
tushaR
8
Może możesz wypróbować pybind11, który jest lekki w porównaniu do boosta .
jdhao,
659

Moduł ctypes jest częścią standardowej biblioteki, a zatem jest bardziej stabilny i szeroko dostępny niż swig , co zawsze sprawiało mi problemy .

W przypadku ctypes musisz spełnić każdą zależność czasową kompilacji od Pythona, a twoje wiązanie będzie działać na każdym pythonie, który ma ctypy, a nie tylko na tym, z którym został skompilowany.

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

#include <iostream>

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

Ponieważ ctypy mogą rozmawiać tylko z funkcjami języka C, musisz podać te, które deklarują je jako zewnętrzne „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 musisz napisać swoje opakowanie 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)

Gdy już to zrobisz, możesz to tak nazwać

f = Foo()
f.bar() #and you will see "Hello" on the screen
Florian Bösch
źródło
14
To jest prawie to, co boost.python robi dla Ciebie w jednym wywołaniu funkcji.
Martin Beckett,
203
ctypes znajduje się w standardowej bibliotece Pythona, swig i boost nie. Swig i boost polegają na modułach rozszerzeń i dlatego są powiązane z mniejszymi wersjami Pythona, które nie są niezależnymi obiektami współdzielonymi. budowanie owijki swig lub boost może być uciążliwe, ctypy nie wymagają żadnych wymagań.
Florian Bösch,
25
boost opiera się na magii szablonu voodoo i całkowicie niestandardowym systemie kompilacji, ctypes polega na prostocie. ctypes jest dynamiczny, boost jest statyczny. ctypy mogą obsługiwać różne wersje bibliotek. Boost nie może.
Florian Bösch,
32
W systemie Windows musiałem podać __declspec (dllexport) w moich podpisach funkcji, aby Python mógł je zobaczyć. Z powyższego przykładu odpowiadałoby to: extern "C" { __declspec(dllexport) Foo* Foo_new(){ return new Foo(); } __declspec(dllexport) void Foo_bar(Foo* foo){ foo->bar(); } }
Alan Macdonald
13
Nie zapomnij usunąć wskaźnika później, np. Udostępniając Foo_deletefunkcję i wywołując ją albo z destruktora Pythona, albo zawijając obiekt w zasobie .
Adversus
58

Najszybszym sposobem na to jest użycie SWIG .

Przykład z samouczka SWIG :

/* 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 Python 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

Stosowanie:

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

Zauważ, że musisz mieć python-dev. Również w niektórych systemach pliki nagłówkowe Pythona będą w /usr/include/python2.7 w zależności od sposobu zainstalowania.

Z samouczka:

SWIG to dość kompletny kompilator C ++ z obsługą prawie wszystkich funkcji językowych. Obejmuje to przetwarzanie wstępne, 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 - odsłaniając podstawową funkcjonalność w bardzo naturalny sposób.

Ben Hoffstein
źródło
50

Swoją podróż rozpocząłem w powiązaniu Pythona <-> C ++ z tej strony, mając na celu połączenie typów danych wysokiego poziomu (wielowymiarowe wektory STL z listami Pythona) :-)

Po wypróbowaniu rozwiązań opartych na obu typach i boost.python (i nie będąc inżynierem oprogramowania) Znalazłem je złożone typy danych, gdy wymagany jest wysoki poziom wiązania, natomiast znalazłem haust znacznie prostsze dla takich przypadków.

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

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

Kod w C ++ („code.cpp”) jest następujący:

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

Odpowiednik nagłówka („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 („code.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"

Za pomocą 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ć bibliotekę współdzieloną, którą można bezpośrednio importować przez Python (ważne jest „_”):

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ć tej funkcji w skryptach 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
Antonello
źródło
Rzeczywista implementacja przypadku, w której w kodzie C ++ wektory stl są przekazywane jako odwołania non const, a zatem są dostępne przez python jako parametry wyjściowe: lobianco.org/antonello/personal:portfolio:portopt
Antonello
30

Sprawdź pyrex lub Cython . Są to języki podobne do Pythona do współpracy między C / C ++ i Python.

Jason Baker
źródło
1
+1 dla Cython! Nie próbowałem cffi, więc nie mogę powiedzieć, co jest lepsze, ale miałem bardzo dobre doświadczenia z Cython - nadal piszesz kod Python, ale możesz w nim używać C. Trochę trudno było mi skonfigurować proces kompilacji z Cython, co później wyjaśniłem w blogu: martinsosic.com/development/2016/02/08/…
Martinsos
Możesz poprawić odpowiedź, aby nie była już odpowiedzią tylko na link.
Adelin
Używam Cython od około tygodnia i bardzo mi się podoba: 1) Widziałem używane typy i jest Brzydki i bardzo podatny na błędy z licznymi pułapkami 2) Pozwala ci wziąć trochę kodu Pythona i przyspieszyć go od samego statycznego wpisywania rzeczy 3) Łatwo jest napisać otoki Pythona dla metod i obiektów C / C ++ 4) Nadal jest dobrze obsługiwany. Mogłoby to przynieść więcej wskazówek dotyczących instalacji w venv i kompilacji krzyżowej, co zajęło trochę czasu. Tutaj jest bardzo dobry 4-godzinny samouczek wideo: youtube.com/watch?v=gMvkiQ-gOW8
Den-Jason
22

Dla współczesnego C ++ użyj cppyy: http://cppyy.readthedocs.io/en/latest/

Opiera się na Cling, interpretatorze C ++ dla Clang / LLVM. Wiązania są w czasie wykonywania i nie jest wymagany żaden 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 przykładu ctypes to ten wątek, ale podzielony na sekcje nagłówka i 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; }

Skompiluj to:

    $ 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 odbiciu i fragmentów cmake do ich utworzenia, aby użytkownicy zainstalowanych pakietów mogli 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.

Wim Lavrijsen
źródło
3
W rzeczywistości tak nie jest: Cython jest językiem programowania podobnym do Pythona do pisania modułów rozszerzeń C dla Pythona (kod Cython zostaje przetłumaczony na C wraz z niezbędną płytką kotłową C-API). Zapewnia podstawową obsługę C ++. Programowanie w cppyy obejmuje tylko Python i C ++, bez rozszerzeń językowych. Jest w pełni działający i nie generuje kodu offline (leniwe generowanie skaluje się znacznie lepiej). Jest ukierunkowany na nowoczesne C ++ (w tym automatyczne instancje szablonów, ruchy, listy inicjalizujące, lambda itp. Itp.), A PyPy jest obsługiwany natywnie (tj. Nie przez wolną warstwę emulacji C-API).
Wim Lavrijsen
2
Ten papier PyHPC'16 zawiera szereg numerów wzorcowych. Od tego czasu nastąpiła jednak wyraźna poprawa po stronie CPython.
Wim Lavrijsen
Lubię to podejście, ponieważ nie trzeba zrobić dodatkową pracę integracji z swig, ctypeslub boost.python. Zamiast pisać kod, aby Python działał z Twoim kodem c ++ ... Python wykonuje ciężką pracę, aby znaleźć c ++. Zakładając, że to faktycznie działa.
Trevor Boyd Smith
cppyy jest bardzo interesujący! W dokumentach widzę, że redystrybucja i pakowanie wstępne są obsługiwane. Czy to dobrze działa z narzędziami, które również pakują kod Pythona (np. PyInstaller)? I czy jest to związane z projektem ROOT, czy może wykorzystać jego pracę?
JimB
Dzięki! PyInstaller nie jest mi znany, ale „słowniki”, które pakują do przodu deklaracje, ścieżki i nagłówki, to kody C ++ kompilowane do współdzielonych bibliotek. Ponieważ cppyy jest używany do wiązania kodu C ++, zakładam, że obsługa trochę więcej kodu C ++ powinna być w porządku. I ten kod nie jest zależny od Python C-API (jest tylko moduł libcppyy), co upraszcza rzeczy. samo cppyy można zainstalować z Conda-Forge lub pypi (pip), więc na pewno każde z tych środowisk będzie działać. Tak, cppyy zaczął jako widelec od PyROOT, ale od tego czasu tak się poprawił, że zespół ROOT wypuszcza PyROOT na cppyy.
Wim Lavrijsen
15

Nigdy go nie używałem, ale słyszałem dobre rzeczy o typach . Jeśli próbujesz używać go z C ++, pamiętaj, aby uniknąć przekłamywania nazw przez extern "C". Dzięki za komentarz, Florian Bösch.

Jan
źródło
13

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 uczenia się trzeciego języka: każda alternatywa wymaga nauki własnego języka (Cython, SWIG) lub API (ctyp). Próbowaliśmy więc założyć, że znasz Python i C i zminimalizować dodatkowe bity API, których musisz się nauczyć.

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

mrgloom
źródło
2
Myślę, że to może wywoływać tylko c (nie c ++), wciąż +1 (bardzo lubię cffi).
Andy Hayden,
8

Pytanie brzmi: jak wywołać funkcję C z Pythona, jeśli dobrze zrozumiałem. Zatem najlepszym wyborem są typy Ctyp (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

Szczegółowy przewodnik możesz znaleźć w moim blogu .

Jadav Bheda
źródło
Warto zauważyć, że chociaż ctypy są przenośne, twój kod wymaga biblioteki C specyficznej dla systemu Windows.
Palec
7

Jeden z oficjalnych dokumentów Pythona zawiera szczegółowe informacje na temat rozszerzania Pythona za pomocą C / C ++ . Nawet bez użycia SWIG jest dość prosty i działa doskonale w systemie Windows.

Andrew Edgecombe
źródło
6

Cython jest zdecydowanie właściwą drogą, chyba że spodziewasz się pisać opakowania Java, w takim przypadku SWIG może być lepszym rozwiązaniem.

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

Oto minimalne przykłady, które stworzyłem przy użyciu obu narzędzi:

https://github.com/nicodjimenez/python2cpp

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

nicodjimenez
źródło
5

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 rozszerzeń binarnych . Przypadki użycia można podzielić na 3 kategorie:

  • moduły akceleratora : aby działały szybciej niż równoważny czysty kod Pythona działa w CPython.
  • moduły opakowujące : w celu udostępnienia istniejących interfejsów C kodowi Python.
  • dostęp do systemu na niskim poziomie : aby uzyskać dostęp do funkcji niższego poziomu środowiska wykonawczego CPython, systemu operacyjnego lub sprzętu.

Aby dać szerszą perspektywę innym zainteresowanym, a 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 korzystania z rozszerzeń binarnych i ich alternatyw.

Oprócz innych sugerowanych odpowiedzi, jeśli chcesz moduł akceleratora, możesz wypróbować Numba . Działa „generując zoptymalizowany kod maszynowy przy użyciu infrastruktury kompilatora LLVM w czasie importu, w czasie wykonywania lub statycznie (przy użyciu dołączonego narzędzia pycc)”.

Jarosław Nikitenko
źródło
3

Uwielbiam cppyy, bardzo ułatwia rozszerzenie Pythona za pomocą kodu C ++, dramatycznie zwiększając wydajność w razie potrzeby.

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

tutaj jest przykład, w jaki sposób można utworzyć tablicę numpy i przekazać ją do funkcji członka 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 także bardzo łatwo stworzyć moduł Pythona (z CMake), dzięki czemu unikniesz ponownej kompilacji kodu C ++ przez cały czas.

Garfield
źródło
2

pybind11 minimalny możliwy do uruchomienia przykład

pybind11 był wcześniej wspomniany na https://stackoverflow.com/a/38542539/895245, ale chciałbym podać tutaj konkretny przykład użycia i trochę dalszej dyskusji na temat implementacji.

W sumie gorąco 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 udostępnić Pythonowi i robi to w przejrzysty sposób.

Minusem tej magii szablonu jest to, że spowalnia kompilację, natychmiast dodając kilka sekund do dowolnego pliku korzystającego z pybind11, patrz na przykład dochodzenie przeprowadzone w tej sprawie . PyTorch zgadza się .

Oto minimalny przykład, który można uruchomić, aby poczuć, jak niesamowity jest pybind11:

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, jak pybind11 pozwala bez wysiłku wystawiać ClassTestklasę C ++ na Pythona! Kompilacja tworzy plik o nazwie, class_test.cpython-36m-x86_64-linux-gnu.soktóry class_test_main.pyautomatycznie wybiera się jako punkt definicji plikuclass_test natywnie zdefiniowanego modułu.

Być może uświadomienie sobie, jak to jest niesamowite, zatapia się tylko wtedy, gdy próbujesz zrobić to samo ręcznie z natywnym API Pythona, zobacz na przykład ten przykład wykonania, który zawiera około 10 razy więcej kodu: https://github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c W tym przykładzie możesz zobaczyć, jak kod C musi boleśnie i wyraźnie definiować klasę Python krok po kroku ze wszystkimi zawartymi w niej informacjami (członkowie, metody, dalej metadane ...). Zobacz też:

pybind11 twierdzi, że jest podobny do Boost.Pythontego, o którym wspomniano na https://stackoverflow.com/a/145436/895245, ale bardziej minimalny, ponieważ jest wolny od nadęty bycia w projekcie Boost:

pybind11 to lekka biblioteka zawierająca tylko nagłówki, 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łej biblioteki Boost.Python autorstwa Davida Abrahamsa: aby zminimalizować kod płyty wzorcowej w tradycyjnych modułach rozszerzeń poprzez wywnioskowanie informacji o typie przy użyciu introspekcji w czasie kompilacji.

Główny problem z Boost.Python - i powód stworzenia takiego podobnego projektu - to Boost. Boost to niezwykle duży i złożony zestaw bibliotek narzędziowych, który działa z prawie każdym istniejącym kompilatorem C ++. Ta kompatybilność ma swój koszt: tajemnicze sztuczki związane z szablonami i obejścia są niezbędne do obsługi najstarszych i najbardziej szkodliwych próbek kompilatora. Teraz, gdy kompilatory kompatybilne z C ++ 11 są powszechnie dostępne, ta ciężka maszyna stała się zbyt dużą i niepotrzebną zależnością.

Pomyśl o tej bibliotece jako o małej, samodzielnej wersji Boost.Python ze wszystkim, co zostało usunięte, co nie ma znaczenia dla generowania powiązań. Bez komentarzy podstawowe pliki nagłówkowe wymagają tylko ~ 4 wierszy kodu i zależą od Pythona (2.7 lub 3.x lub PyPy2.7> = 5.7) i standardowej biblioteki C ++. Ta kompaktowa implementacja była możliwa dzięki niektórym nowym funkcjom języka C ++ 11 (w szczególności: krotki, funkcje lambda i szablony variadic). Od momentu powstania biblioteka ta rozszerzyła się na wiele sposobów poza Boost.Python, prowadząc do znacznie prostszego wiązania kodu w wielu typowych sytuacjach.

pybind11 jest także jedyną nie-rodzimą alternatywą wyróżnioną przez aktualną dokumentację wiązania 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.

Ciro Santilli
źródło