Réponses:
Vous devriez jeter un œil à Boost.Python . Voici la courte introduction tirée de leur site Web:
La bibliothèque Boost Python est un cadre pour interfacer Python et C ++. Il vous permet d'exposer rapidement et de manière transparente des fonctions et des objets de classes C ++ à Python, et vice-versa, sans utiliser d'outils spéciaux - juste votre compilateur C ++. Il est conçu pour encapsuler les interfaces C ++ de manière non intrusive, de sorte que vous ne devriez pas avoir à modifier du tout le code C ++ afin de l'encapsuler, ce qui rend Boost.Python idéal pour exposer des bibliothèques tierces à Python. L'utilisation par la bibliothèque de techniques avancées de métaprogrammation simplifie sa syntaxe pour les utilisateurs, de sorte que le code d'habillage prend l'apparence d'une sorte de langage de définition d'interface déclarative (IDL).
Le module ctypes fait partie de la bibliothèque standard et est donc plus stable et largement disponible que swig , ce qui a toujours eu tendance à me poser des problèmes .
Avec les ctypes, vous devez satisfaire toute dépendance de temps de compilation sur python, et votre liaison fonctionnera sur n'importe quel python qui a des ctypes, pas seulement celui avec lequel il a été compilé.
Supposons que vous ayez un exemple de classe C ++ simple dans lequel vous souhaitez parler dans un fichier appelé foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Comme les ctypes ne peuvent parler qu'aux fonctions C, vous devez fournir ceux qui les déclarent comme "C" externe
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Ensuite, vous devez compiler cela dans une bibliothèque partagée
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
Et enfin, vous devez écrire votre wrapper python (par exemple dans 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)
Une fois que vous l'avez, vous pouvez l'appeler comme
f = Foo()
f.bar() #and you will see "Hello" on the screen
extern "C" { __declspec(dllexport) Foo* Foo_new(){ return new Foo(); } __declspec(dllexport) void Foo_bar(Foo* foo){ foo->bar(); } }
La façon la plus rapide de le faire est d'utiliser SWIG .
Exemple du didacticiel SWIG :
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Fichier d'interface:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Construire un module Python sous 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
Usage:
>>> import example
>>> example.fact(5)
120
Notez que vous devez avoir python-dev. Dans certains systèmes, les fichiers d'en-tête python seront également dans /usr/include/python2.7 en fonction de la façon dont vous l'avez installé.
Du tutoriel:
SWIG est un compilateur C ++ assez complet avec prise en charge de presque toutes les fonctionnalités de langage. Cela inclut le prétraitement, les pointeurs, les classes, l'héritage et même les modèles C ++. SWIG peut également être utilisé pour empaqueter des structures et des classes dans des classes proxy dans le langage cible - exposant la fonctionnalité sous-jacente de manière très naturelle.
J'ai commencé mon voyage dans la liaison Python <-> C ++ à partir de cette page, dans le but de lier des types de données de haut niveau (vecteurs STL multidimensionnels avec des listes Python) :-)
Ayant essayé les solutions basées à la fois sur ctypes et boost.python (et n'étant pas ingénieur logiciel), je les ai trouvées complexes lorsqu'une liaison de types de données de haut niveau est requise, alors que j'ai trouvé SWIG beaucoup plus simple pour de tels cas.
Cet exemple utilise donc SWIG, et il a été testé sous Linux (mais SWIG est disponible et est également largement utilisé sous Windows).
L'objectif est de mettre à la disposition de Python une fonction C ++ qui prend une matrice sous la forme d'un vecteur STL 2D et renvoie une moyenne de chaque ligne (en tant que vecteur STL 1D).
Le code en C ++ ("code.cpp") est le suivant:
#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;
}
L'en-tête équivalent ("code.h") est:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Nous compilons d'abord le code C ++ pour créer un fichier objet:
g++ -c -fPIC code.cpp
Nous définissons ensuite un fichier de définition d'interface SWIG ("code.i") pour nos fonctions 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"
À l'aide de SWIG, nous générons un code source d'interface C ++ à partir du fichier de définition d'interface SWIG.
swig -c++ -python code.i
Nous compilons enfin le fichier source de l'interface C ++ généré et lions tout ensemble pour générer une bibliothèque partagée directement importable par Python (le "_" compte):
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
Nous pouvons maintenant utiliser la fonction dans les scripts 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
Il y a aussi pybind11
, qui est comme une version allégée de Boost.Python et compatible avec tous les compilateurs C ++ modernes:
Pytorch
pytorch.org/tutorials/advanced/cpp_extension.html Fonctionne également entièrement sur VS Community
Windows
Check-out pyrex ou Cython . Ce sont des langages de type Python pour l'interfaçage entre C / C ++ et Python.
Pour le C ++ moderne, utilisez cppyy: http://cppyy.readthedocs.io/en/latest/
Il est basé sur Cling, l'interpréteur C ++ pour Clang / LLVM. Les liaisons sont au moment de l'exécution et aucun langage intermédiaire supplémentaire n'est nécessaire. Grâce à Clang, il prend en charge C ++ 17.
Installez-le en utilisant pip:
$ pip install cppyy
Pour les petits projets, chargez simplement la bibliothèque appropriée et les en-têtes qui vous intéressent. Par exemple, prenez le code de l'exemple ctypes est ce fil, mais divisé en sections d'en-tête et de code:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Compilez-le:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
et l'utiliser:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Les grands projets sont pris en charge avec le chargement automatique des informations de réflexion préparées et les fragments cmake pour les créer, afin que les utilisateurs des packages installés puissent simplement exécuter:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
Grâce à LLVM, des fonctionnalités avancées sont possibles, telles que l'instanciation automatique des modèles. Pour continuer l'exemple:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Remarque: je suis l'auteur de cppyy.
swig
, ctypes
ou boost.python
. Au lieu d'avoir à écrire du code pour que python fonctionne avec votre code c ++ ... python fait le travail difficile pour comprendre c ++. En supposant que cela fonctionne réellement.
Cet article, affirmant que Python est tout ce dont un scientifique a besoin , dit essentiellement: Tout d'abord prototype de tout en Python. Ensuite, lorsque vous devez accélérer une partie, utilisez SWIG et traduisez cette partie en C.
Je ne l'ai jamais utilisé mais j'ai entendu de bonnes choses sur les types de c . Si vous essayez de l'utiliser avec C ++, assurez-vous d'éviter le changement de nom via extern "C"
. Merci pour le commentaire, Florian Bösch.
Je pense que cffi pour python peut être une option.
Le but est d'appeler du code C depuis Python. Vous devriez pouvoir le faire sans apprendre une troisième langue: chaque alternative vous oblige à apprendre leur propre langage (Cython, SWIG) ou API (ctypes). Nous avons donc essayé de supposer que vous connaissez Python et C et de minimiser les bits supplémentaires d'API que vous devez apprendre.
La question est de savoir comment appeler une fonction C à partir de Python, si j'ai bien compris. Ensuite, le meilleur pari est Ctypes (BTW portable sur toutes les variantes de Python).
>>> 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
Pour un guide détaillé, vous pouvez vous référer à mon article de blog .
L'un des documents officiels Python contient des détails sur l' extension de Python à l'aide de C / C ++ . Même sans l'utilisation de SWIG , c'est assez simple et fonctionne parfaitement bien sous Windows.
Cython est certainement la voie à suivre, sauf si vous prévoyez d'écrire des wrappers Java, auquel cas SWIG peut être préférable.
Je recommande d'utiliser l' runcython
utilitaire de ligne de commande, cela rend le processus d'utilisation de Cython extrêmement simple. Si vous devez transmettre des données structurées à C ++, jetez un œil à la bibliothèque de protobuf de Google, c'est très pratique.
Voici quelques exemples minimes que j'ai faits qui utilisent les deux outils:
https://github.com/nicodjimenez/python2cpp
J'espère que cela peut être un point de départ utile.
Vous devez d'abord décider quel est votre objectif particulier. La documentation officielle de Python sur l' extension et l'incorporation de l'interpréteur Python a été mentionnée ci-dessus, je peux ajouter un bon aperçu des extensions binaires . Les cas d'utilisation peuvent être divisés en 3 catégories:
Afin de donner une perspective plus large aux autres personnes intéressées et puisque votre question initiale est un peu vague ("à une bibliothèque C ou C ++"), je pense que cette information pourrait vous intéresser. Sur le lien ci-dessus, vous pouvez lire les inconvénients de l'utilisation des extensions binaires et de ses alternatives.
Outre les autres réponses suggérées, si vous voulez un module accélérateur, vous pouvez essayer Numba . Il fonctionne "en générant du code machine optimisé à l'aide de l'infrastructure du compilateur LLVM au moment de l'importation, de l'exécution ou de façon statique (à l'aide de l'outil pycc inclus)".
J'adore cppyy, il est très facile d'étendre Python avec du code C ++, augmentant considérablement les performances en cas de besoin.
Il est puissant et franchement très simple à utiliser,
ici, c'est un exemple de la façon dont vous pouvez créer un tableau numpy et le passer à une fonction membre de classe en 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])
Buffer.h
struct Buffer {
void get_numpy_array(double *ad, int size) {
for( long i=0; i < size; i++)
ad[i]=i;
}
};
Vous pouvez également créer un module Python très facilement (avec CMake), de cette façon vous éviterez de recompiler le code C ++ tout le temps.
exemple exécutable minimal pybind11
pybind11 a déjà été mentionné sur https://stackoverflow.com/a/38542539/895245 mais je voudrais donner ici un exemple d'utilisation concret et quelques discussions supplémentaires sur la mise en œuvre.
Dans l'ensemble, je recommande fortement pybind11 car il est vraiment facile à utiliser: vous incluez simplement un en-tête, puis pybind11 utilise la magie des modèles pour inspecter la classe C ++ que vous souhaitez exposer à Python et le fait de manière transparente.
L'inconvénient de ce modèle magique est qu'il ralentit la compilation en ajoutant immédiatement quelques secondes à tout fichier utilisant pybind11, voir par exemple l'enquête effectuée sur ce problème . PyTorch est d'accord .
Voici un exemple exécutable minimal pour vous donner une idée de la qualité de 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)
Compiler et exécuter:
#!/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
Cet exemple montre comment pybind11 vous permet d'exposer sans effort la ClassTest
classe C ++ à Python! La compilation produit un fichier nommé class_test.cpython-36m-x86_64-linux-gnu.so
qui class_test_main.py
prend automatiquement le point de définition duclass_test
module défini nativement.
Peut-être que la réalisation de la façon dont c'est génial ne s'enfonce que si vous essayez de faire la même chose à la main avec l'API Python native, voir par exemple cet exemple de faire cela, qui a environ 10 fois plus de code: https://github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c Sur cet exemple, vous pouvez voir comment le code C doit définir douloureusement et explicitement la classe Python bit par bit avec toutes les informations qu'il contient (membres, méthodes, plus loin) métadonnées ...). Voir également:
pybind11 prétend être similaire à Boost.Python
ce qui a été mentionné sur https://stackoverflow.com/a/145436/895245 mais plus minimal car il est libéré du ballonnement d'être à l'intérieur du projet Boost:
pybind11 est une bibliothèque légère uniquement en-tête qui expose les types C ++ en Python et vice versa, principalement pour créer des liaisons Python de code C ++ existant. Ses objectifs et sa syntaxe sont similaires à l'excellente bibliothèque Boost.Python de David Abrahams: minimiser le code passe-partout dans les modules d'extension traditionnels en déduisant les informations de type à l'aide d'une introspection au moment de la compilation.
Le principal problème avec Boost.Python - et la raison de la création d'un projet similaire - est Boost. Boost est une suite de bibliothèques d'utilitaires extrêmement vaste et complexe qui fonctionne avec presque tous les compilateurs C ++ existants. Cette compatibilité a un coût: des trucs et des solutions de rechange arcaniques sont nécessaires pour prendre en charge les spécimens de compilateur les plus anciens et les plus bogues. Maintenant que les compilateurs compatibles C ++ 11 sont largement disponibles, cette machinerie lourde est devenue une dépendance excessivement grande et inutile.
Considérez cette bibliothèque comme une petite version autonome de Boost.Python avec tout ce qui n'est pas pertinent pour la génération de liaisons. Sans commentaires, les fichiers d'en-tête de base ne nécessitent que ~ 4K lignes de code et dépendent de Python (2.7 ou 3.x, ou PyPy2.7> = 5.7) et de la bibliothèque standard C ++. Cette implémentation compacte a été possible grâce à certaines des nouvelles fonctionnalités du langage C ++ 11 (en particulier: tuples, fonctions lambda et modèles variadic). Depuis sa création, cette bibliothèque s'est développée au-delà de Boost.Python à bien des égards, conduisant à un code de liaison considérablement plus simple dans de nombreuses situations courantes.
pybind11 est également la seule alternative non native mise en évidence par la documentation de liaison Microsoft Python C actuelle sur: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in- visual-studio? view = vs-2019 ( archive ).
Testé sur Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0.