Jawaban:
Anda harus melihat Boost.Python . Berikut adalah pengantar singkat yang diambil dari situs web mereka:
Perpustakaan Boost Python adalah kerangka kerja untuk menghubungkan Python dan C ++. Ini memungkinkan Anda untuk dengan cepat dan mulus mengekspos fungsi kelas C ++ dan objek ke Python, dan sebaliknya, tanpa menggunakan alat khusus - hanya kompiler C ++ Anda. Ini dirancang untuk membungkus antarmuka C ++ non-intrusively, sehingga Anda tidak perlu mengubah kode C ++ sama sekali untuk membungkusnya, membuat Boost.Python ideal untuk mengekspos perpustakaan pihak ke-3 ke Python. Penggunaan teknik metaprogramming tingkat lanjut dari perpustakaan menyederhanakan sintaksnya untuk pengguna, sehingga kode pembungkus terlihat seperti jenis bahasa definisi antarmuka deklaratif (IDL).
Modul ctypes adalah bagian dari library standar, dan karenanya lebih stabil dan tersedia secara luas daripada swig , yang selalu cenderung memberi saya masalah. .
Dengan ctypes, Anda harus memenuhi setiap dependensi waktu kompilasi pada python, dan pengikatan Anda akan bekerja pada python yang memiliki ctypes, bukan hanya kompilasi yang dikompilasi.
Misalkan Anda memiliki kelas contoh C ++ sederhana yang ingin Anda ajak bicara dalam file bernama foo.cpp:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Karena ctypes hanya dapat berbicara dengan fungsi C, Anda perlu menyediakan yang menyatakannya sebagai "C" dari luar
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Selanjutnya Anda harus mengkompilasi ini ke perpustakaan bersama
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
Dan akhirnya Anda harus menulis pembungkus python Anda (misalnya di 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)
Setelah Anda memilikinya Anda dapat menyebutnya seperti
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(); } }
Foo_delete
fungsi dan memanggilnya baik dari penghancur python atau membungkus objek dalam sumber daya .
Cara tercepat untuk melakukan ini adalah menggunakan SWIG .
Contoh dari tutorial SWIG :
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
File antarmuka:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Membangun modul Python di 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
Pemakaian:
>>> import example
>>> example.fact(5)
120
Perhatikan bahwa Anda harus memiliki python-dev. Juga di beberapa sistem file header python akan berada di /usr/include/python2.7 berdasarkan cara Anda menginstalnya.
Dari tutorial:
SWIG adalah kompiler C ++ yang cukup lengkap dengan dukungan untuk hampir setiap fitur bahasa. Ini termasuk preprocessing, pointer, kelas, warisan, dan bahkan template C ++. SWIG juga dapat digunakan untuk mengemas struktur dan kelas menjadi kelas proksi dalam bahasa target - mengekspos fungsi yang mendasarinya dengan cara yang sangat alami.
Saya memulai perjalanan saya di Python <-> C ++ mengikat dari halaman ini, dengan tujuan menghubungkan tipe data tingkat tinggi (vektor STL multidimensi dengan daftar Python) :-)
Setelah mencoba solusi berdasarkan ctypes dan boost.python (dan tidak menjadi insinyur perangkat lunak) saya telah menemukan mereka kompleks ketika diperlukan mengikat tipe data tingkat tinggi, sementara saya telah menemukan SWIG jauh lebih sederhana untuk kasus-kasus seperti itu.
Oleh karena itu, contoh ini menggunakan SWIG, dan telah diuji di Linux (tetapi SWIG tersedia dan banyak digunakan di Windows juga).
Tujuannya adalah untuk membuat fungsi C ++ tersedia untuk Python yang mengambil matriks dalam bentuk vektor STL 2D dan mengembalikan rata-rata setiap baris (sebagai vektor 1D STL).
Kode dalam C ++ ("code.cpp") adalah sebagai berikut:
#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;
}
Header yang setara ("code.h") adalah:
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Kami pertama-tama mengkompilasi kode C ++ untuk membuat file objek:
g++ -c -fPIC code.cpp
Kami kemudian mendefinisikan file definisi antarmuka SWIG ("code.i") untuk fungsi C ++ kami.
%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"
Menggunakan SWIG, kami membuat kode sumber antarmuka C ++ dari file definisi antarmuka SWIG ..
swig -c++ -python code.i
Kami akhirnya mengkompilasi file sumber antarmuka C ++ yang dihasilkan dan menautkan semuanya untuk menghasilkan pustaka bersama yang secara langsung dapat diimpor oleh Python (hal-hal "_"):
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
Kita sekarang dapat menggunakan fungsi dalam skrip 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
Ada juga pybind11
, yang seperti versi ringan dari Boost.Python dan kompatibel dengan semua kompiler C ++ modern:
Pytorch
pytorch.org/tutorials/advanced/cpp_extension.html Juga sepenuhnya berfungsi pada VS Community
Windows
Lihat pyrex atau Cython . Mereka adalah bahasa seperti Python untuk antarmuka antara C / C ++ dan Python.
Untuk C ++ modern, gunakan cppyy: http://cppyy.readthedocs.io/en/latest/
Ini berdasarkan Cling, juru bahasa C ++ untuk Dentang / LLVM. Binding pada saat run-time dan tidak ada bahasa perantara tambahan yang diperlukan. Berkat Dentang, ini mendukung C ++ 17.
Instal menggunakan pip:
$ pip install cppyy
Untuk proyek-proyek kecil, cukup muat pustaka yang relevan dan tajuk yang Anda minati. Misalnya ambil kode dari ctypes contohnya adalah utas ini, tetapi bagi bagian header dan kode:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Kompilasi:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
dan gunakan:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Proyek besar didukung dengan pemuatan otomatis informasi refleksi yang disiapkan dan fragmen cmake untuk membuatnya, sehingga pengguna paket yang diinstal dapat dengan mudah menjalankan:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
Berkat LLVM, fitur-fitur canggih dimungkinkan, seperti contoh template otomatis. Untuk melanjutkan contoh:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Catatan: Saya penulis cppyy.
swig
, ctypes
atau boost.python
. Alih-alih Anda harus menulis kode untuk membuat python bekerja dengan kode c ++ Anda ... python bekerja keras untuk mencari tahu c ++. Dengan asumsi itu benar-benar berfungsi.
Makalah ini, mengklaim Python sebagai semua yang dibutuhkan oleh para ilmuwan , pada dasarnya mengatakan: Pertama-tama prototipe segala sesuatu dalam Python. Kemudian ketika Anda perlu mempercepat bagian, gunakan SWIG dan terjemahkan bagian ini ke C.
Saya tidak pernah menggunakannya tetapi saya pernah mendengar hal-hal baik tentang ctypes . Jika Anda mencoba menggunakannya dengan C ++, pastikan untuk menghindari pengubahan nama via extern "C"
. Terima kasih atas komentarnya, Florian Bösch.
Saya pikir cffi untuk python dapat menjadi pilihan.
Tujuannya adalah untuk memanggil kode C dari Python. Anda harus dapat melakukannya tanpa mempelajari bahasa ketiga: setiap alternatif mengharuskan Anda mempelajari bahasa mereka sendiri (Cython, SWIG) atau API (ctypes). Jadi kami mencoba berasumsi bahwa Anda tahu Python dan C dan meminimalkan bit tambahan API yang perlu Anda pelajari.
Pertanyaannya adalah bagaimana memanggil fungsi C dari Python, jika saya mengerti dengan benar. Maka taruhan terbaik adalah Ctypes (BTW portable di semua varian 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
Untuk panduan terperinci Anda mungkin ingin merujuk ke artikel blog saya .
Salah satu dokumen Python resmi berisi detail tentang cara memperpanjang Python menggunakan C / C ++ . Bahkan tanpa menggunakan SWIG , itu cukup mudah dan berfungsi dengan baik pada Windows.
Cython jelas merupakan cara yang harus ditempuh, kecuali Anda mengantisipasi penulisan pembungkus Java, dalam hal ini SWIG mungkin lebih disukai.
Saya sarankan menggunakan runcython
utilitas baris perintah, itu membuat proses menggunakan Cython sangat mudah. Jika Anda perlu mengirimkan data terstruktur ke C ++, lihat perpustakaan protobuf Google, itu sangat nyaman.
Berikut adalah contoh minimal yang saya buat yang menggunakan kedua alat:
https://github.com/nicodjimenez/python2cpp
Semoga ini bisa menjadi titik awal yang bermanfaat.
Pertama, Anda harus memutuskan apa tujuan khusus Anda. Dokumentasi Python resmi tentang memperluas dan menanamkan interpreter Python telah disebutkan di atas, saya dapat menambahkan ikhtisar yang baik tentang ekstensi biner . Kasus penggunaan dapat dibagi menjadi 3 kategori:
Untuk memberikan perspektif yang lebih luas bagi orang lain yang tertarik dan karena pertanyaan awal Anda agak kabur ("ke perpustakaan C atau C ++"), saya pikir informasi ini mungkin menarik bagi Anda. Pada tautan di atas Anda dapat membaca tentang kerugian menggunakan ekstensi biner dan alternatifnya.
Terlepas dari jawaban lain yang disarankan, jika Anda menginginkan modul akselerator, Anda dapat mencoba Numba . Ia bekerja "dengan menghasilkan kode mesin yang dioptimalkan menggunakan infrastruktur kompiler LLVM pada waktu impor, runtime, atau secara statis (menggunakan alat pycc yang disertakan)".
Saya suka cppyy, membuatnya sangat mudah untuk memperpanjang Python dengan kode C ++, secara dramatis meningkatkan kinerja saat dibutuhkan.
Ini kuat dan terus terang sangat mudah digunakan,
ini dia contoh bagaimana Anda bisa membuat array numpy dan meneruskannya ke fungsi anggota kelas di 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;
}
};
Anda juga dapat membuat modul Python dengan sangat mudah (dengan CMake), dengan cara ini Anda akan menghindari kompilasi ulang kode C ++ setiap saat.
pybind11 contoh runnable minimal
pybind11 sebelumnya disebutkan di https://stackoverflow.com/a/38542539/895245 tapi saya ingin memberikan contoh penggunaan konkret di sini dan beberapa diskusi lebih lanjut tentang implementasi.
Semua dan semua, saya sangat merekomendasikan pybind11 karena sangat mudah digunakan: Anda hanya menyertakan header dan kemudian pybind11 menggunakan templat ajaib untuk memeriksa kelas C ++ yang ingin Anda tampilkan ke Python dan melakukan itu secara transparan.
Kelemahan dari templat templat ini adalah memperlambat kompilasi dengan segera menambahkan beberapa detik ke file apa pun yang menggunakan pybind11, lihat misalnya penyelidikan yang dilakukan pada masalah ini . PyTorch setuju .
Berikut adalah contoh runnable minimal untuk memberi Anda perasaan betapa kerennya 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)
Kompilasi dan jalankan:
#!/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
Contoh ini menunjukkan bagaimana pybind11 memungkinkan Anda dengan mudah mengekspos kelas ClassTest
C ++ ke Python! Kompilasi menghasilkan file bernama class_test.cpython-36m-x86_64-linux-gnu.so
yang class_test_main.py
secara otomatis mengambil sebagai titik definisi untukclass_test
modul yang didefinisikan secara asli.
Mungkin realisasi betapa hebatnya ini hanya meresap jika Anda mencoba melakukan hal yang sama dengan tangan dengan Python API asli, lihat contoh ini, yang memiliki sekitar 10x lebih banyak kode: https://github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c Pada contoh itu Anda dapat melihat bagaimana kode C harus dengan menyakitkan dan secara eksplisit mendefinisikan kelas Python sedikit demi sedikit dengan semua informasi yang dikandungnya (anggota, metode, metode lebih lanjut, metode metadata ...). Lihat juga:
pybind11 mengklaim serupa dengan Boost.Python
yang disebutkan di https://stackoverflow.com/a/145436/895245 tetapi lebih minimal karena dibebaskan dari kegembiraan berada di dalam proyek Boost:
pybind11 adalah pustaka header-only ringan yang memperlihatkan tipe C ++ dalam Python dan sebaliknya, terutama untuk membuat binding Python dari kode C ++ yang ada. Tujuan dan sintaksnya mirip dengan pustaka Boost.Python yang sangat baik oleh David Abrahams: untuk meminimalkan kode boilerplate dalam modul ekstensi tradisional dengan menyimpulkan informasi jenis menggunakan intropeksi waktu kompilasi.
Masalah utama dengan Boost.Python — dan alasan untuk menciptakan proyek serupa — adalah Boost. Boost adalah rangkaian pustaka utilitas yang sangat besar dan kompleks yang bekerja dengan hampir setiap kompiler C ++ yang ada. Kompatibilitas ini memiliki biayanya: diperlukan trik trik dan solusi templat arcane untuk mendukung spesimen kompiler tertua dan paling bug. Sekarang karena kompiler yang kompatibel dengan C ++ 11 tersedia secara luas, alat berat ini telah menjadi ketergantungan yang terlalu besar dan tidak perlu.
Pikirkan perpustakaan ini sebagai Boost.Python versi mandiri yang kecil dengan segala sesuatu yang dilepaskan yang tidak relevan untuk pembuatan generasi. Tanpa komentar, file header inti hanya membutuhkan ~ 4K baris kode dan bergantung pada Python (2.7 atau 3.x, atau PyPy2.7> = 5.7) dan pustaka standar C ++. Implementasi yang ringkas ini dimungkinkan berkat beberapa fitur bahasa C ++ 11 yang baru (khususnya: tuple, fungsi lambda, dan templat variadic). Sejak dibuat, perpustakaan ini telah berkembang melampaui Boost.Python dalam banyak hal, yang mengarah ke kode pengikat yang secara dramatis lebih sederhana dalam banyak situasi umum.
pybind11 juga merupakan satu-satunya alternatif non-asli yang disorot oleh dokumentasi mengikat Microsoft Python C saat ini di: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in- visual-studio? view = vs-2019 ( arsip ).
Diuji pada Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0.