Yanıtlar:
Boost.Python'a bir göz atmalısınız . Web sitelerinden alınan kısa tanıtım:
Boost Python Kütüphanesi, Python ve C ++ ile arayüz oluşturmak için bir çerçevedir. Özel bir araç kullanmadan C ++ sınıf işlevlerini ve nesnelerini Python'a ve tam tersine hızlı ve sorunsuz bir şekilde ortaya çıkarmanızı sağlar - sadece C ++ derleyiciniz. C ++ arabirimlerini müdahaleci olmayan bir şekilde sarmak için tasarlanmıştır, böylece C ++ kodunu sarmak için değiştirmek zorunda kalmamalısınız, Boost.Python'u 3. taraf kitaplıklarını Python'a göstermek için ideal hale getirir. Kütüphanenin gelişmiş metaprogramlama tekniklerini kullanması, kullanıcılar için sözdizimini basitleştirir, böylece sarma kodu bir tür bildirimsel arabirim tanımlama dili (IDL) görünümü alır.
ctypes modülü, standart kütüphanenin bir parçasıdır ve bu nedenle her zaman sorun yaşama eğiliminde olan swig'ten daha kararlı ve yaygın olarak mevcuttur .
Ctypes ile, python'a herhangi bir derleme zamanı bağımlılığını tatmin etmeniz gerekir ve bağlantınız sadece derlendiği gibi değil, ctype'leri olan herhangi bir python üzerinde çalışır.
Foo.cpp adlı bir dosyada konuşmak istediğiniz basit bir C ++ örnek sınıfınız olduğunu varsayın:
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Ctypes sadece C fonksiyonları ile konuşabildiğinden, onları "C" extern olarak ilan edenlere sağlamanız gerekir
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
Sonra bunu paylaşılan bir kütüphaneye derlemelisiniz
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
Ve son olarak python paketleyicinizi yazmalısınız (örn. 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)
Bunu yaptıktan sonra şöyle diyebilirsiniz
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(); } }
Bunu yapmanın en hızlı yolu SWIG kullanmaktır .
SWIG Örneği eğitiminden :
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
Arayüz dosyası:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
Unix üzerinde bir Python modülü oluşturma:
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
Kullanımı:
>>> import example
>>> example.fact(5)
120
Python-dev olması gerektiğini unutmayın. Ayrıca bazı sistemlerde python başlık dosyaları /usr/include/python2.7 dizininde kurulum şekline göre olacaktır.
Eğiticiden:
SWIG, neredeyse her dil özelliğini destekleyen oldukça eksiksiz bir C ++ derleyicisidir. Bu, önişleme, işaretçiler, sınıflar, kalıtım ve hatta C ++ şablonlarını içerir. SWIG, yapıları ve sınıfları hedef dilde proxy sınıflarına paketlemek için de kullanılabilir - altta yatan işlevselliği çok doğal bir şekilde ortaya koyar.
Yüksek düzey veri türlerini (çok boyutlu STL vektörlerini Python listeleriyle) bağlamak amacıyla yolculuğuma bu sayfadan Python <-> C ++ bağlamasında başladım :-)
Her iki dayalı çözümler denenmiş olması ctypes ve boost.python üst düzey veri türleri gereklidir bağlanma ne zaman bulduk iken (ve yazılım mühendisi olmak değil) ben onları karmaşık bulduk yudum böyle durumlar için çok daha basit.
Bu örnekte SWIG kullanılmaktadır ve Linux'ta test edilmiştir (ancak SWIG kullanılabilir ve Windows'da da yaygın olarak kullanılmaktadır).
Amaç, bir 2D STL vektörü biçiminde bir matrisi alan ve her satırın ortalamasını (1D STL vektörü olarak) döndüren Python için bir C ++ işlevini kullanılabilir hale getirmektir.
C ++ ("code.cpp") içindeki kod aşağıdaki gibidir:
#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;
}
Eşdeğer başlık ("code.h"):
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
Önce bir nesne dosyası oluşturmak için C ++ kodunu derleriz:
g++ -c -fPIC code.cpp
Daha sonra C ++ fonksiyonlarımız için bir SWIG arayüz tanım dosyası ("code.i") tanımlarız .
%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"
SWIG kullanarak, SWIG arayüz tanımı dosyasından bir C ++ arayüz kaynak kodu üretiyoruz.
swig -c++ -python code.i
Sonunda oluşturulan C ++ arayüz kaynak dosyasını derliyoruz ve Python tarafından doğrudan içe aktarılabilen paylaşılan bir kütüphane oluşturmak için her şeyi birbirine bağlıyoruz ("_" önemlidir):
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
Şimdi işlevi Python komut dosyalarında kullanabiliriz:
#!/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
Ayrıca Boost.Python'unpybind11
hafif bir versiyonu gibi ve tüm modern C ++ derleyicileriyle uyumlu:
Pytorch
pytorch.org/tutorials/advanced/cpp_extension.html gibi, tam olarak VS Community
Windows üzerinde çalışır
Check out Pyrex'tir veya Cython . C / C ++ ve Python arasındaki arayüz için Python benzeri dillerdir.
Modern C ++ için cppyy kullanın: http://cppyy.readthedocs.io/en/latest/
Clang / LLVM için C ++ tercümanı Cling'e dayanmaktadır. Bağlamalar çalışma zamanındadır ve ek ara dile gerek yoktur. Clang sayesinde C ++ 17'yi destekler.
Pip kullanarak kurun:
$ pip install cppyy
Küçük projeler için, ilgili kütüphaneyi ve ilgilendiğiniz başlıkları yükleyin. Örneğin, kodu ctypes örneğinden alın bu iş parçacığıdır, ancak üstbilgi ve kod bölümlerine ayrılmıştır:
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
Derleyin:
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
ve kullanın:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
Büyük projeler, hazırlanan yansıma bilgilerinin otomatik olarak yüklenmesiyle ve bunları oluşturmak için cmake parçalarıyla desteklenir, böylece kurulu paketlerin kullanıcıları şunları yapabilir:
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
LLVM sayesinde, otomatik şablon örnekleme gibi gelişmiş özellikler mümkündür. Örneğe devam etmek için:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
Not: Ben cppyy'nin yazarıyım.
swig
, ctypes
ya da boost.python
. Bunun yerine python c ++ kodu ile çalışmak için kod yazmak zorunda ... python c ++ anlamaya zor işi yapar. Aslında işe yaradığını varsayarsak.
Python'un bir bilim insanının ihtiyacı olduğunu iddia eden bu makale temel olarak şöyle diyor: İlk önce Python'daki her şeyi prototipleyin. Sonra bir parçayı hızlandırmanız gerektiğinde SWIG kullanın ve bu parçayı C'ye çevirin.
Hiç kullanmadım ama tipler hakkında iyi şeyler duydum . C ++ ile kullanmaya çalışıyorsanız, ad yönetimi ile kaçmayı unutmayın extern "C"
. Yorum için teşekkürler Florian Bösch.
Bence python için cffi bir seçenek olabilir.
Amaç Python'dan C kodu çağırmak. Bunu 3. bir dil öğrenmeden yapabilmelisiniz: her alternatif kendi dilini (Cython, SWIG) veya API (ctypes) öğrenmenizi gerektirir. Bu yüzden, Python ve C'yi bildiğinizi ve öğrenmeniz gereken API'nin fazla bitlerini en aza indirdiğimizi varsaymaya çalıştık.
Soru, doğru anladıysam, Python'dan bir C işlevinin nasıl çağrılacağıdır. O zaman en iyi bahis Ctypes'tir (Python'un tüm çeşitlerinde BTW taşınabilir).
>>> 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
Ayrıntılı bir rehber için blog makaleme bakabilirsiniz .
Resmi Python belgelerinden biri, C / C ++ kullanarak Python'u genişletme hakkında ayrıntılar içerir . SWIG kullanmadan bile , oldukça basittir ve Windows'ta mükemmel çalışır.
Java sarmalayıcıları yazmayı beklemediğiniz sürece Cython kesinlikle gitmenin bir yoludur, bu durumda SWIG tercih edilebilir.
Kullanmanızı tavsiye ederim runcython
son derece kolay Cython kullanma işlemini yapar, komut satırı yardımcı. Yapılandırılmış verileri C ++ 'a iletmeniz gerekiyorsa, Google'ın protobuf kütüphanesine bir göz atın, çok uygun.
Her iki aracı da kullanarak yaptığım minimal örnekler:
https://github.com/nicodjimenez/python2cpp
Umarım faydalı bir başlangıç noktası olabilir.
Öncelikle özel amacınızın ne olduğuna karar vermelisiniz. Python yorumlayıcısını genişletme ve gömme ile ilgili resmi Python belgeleri yukarıda belirtilmiştir, ikili uzantılara iyi bir genel bakış ekleyebilirim . Kullanım durumları 3 kategoriye ayrılabilir:
Diğer ilgililere daha geniş bir bakış açısı vermek için ve ilk sorunuz biraz belirsiz olduğundan ("C veya C ++ kütüphanesine") bu bilginin sizin için ilginç olabileceğini düşünüyorum. Yukarıdaki bağlantıda, ikili uzantıları ve alternatiflerini kullanmanın dezavantajlarını okuyabilirsiniz.
Önerilen diğer cevapların yanı sıra, bir hızlandırıcı modülü istiyorsanız, Numba'yı deneyebilirsiniz . "Alma zamanında, çalışma zamanında veya statik olarak (dahil edilen pycc aracını kullanarak) LLVM derleyici altyapısını kullanarak optimize edilmiş makine kodu oluşturarak çalışır".
Cppyy'yi seviyorum, C ++ kodu ile Python'u genişletmeyi çok kolay hale getiriyor, gerektiğinde performansı önemli ölçüde artırıyor.
Güçlü ve açıkçası kullanımı çok basit,
burada nasıl bir numpy dizi oluşturmak ve C ++ sınıf üyesi işlevine geçirebilirsiniz bir örnektir.
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;
}
};
Ayrıca çok kolay bir şekilde (CMake ile) bir Python modülü oluşturabilirsiniz, böylece C ++ kodunu her zaman yeniden derlemekten kaçınabilirsiniz.
pybind11 minimal çalıştırılabilir örnek
pybind11 daha önce https://stackoverflow.com/a/38542539/895245 adresinde belirtilmişti, ancak burada somut bir kullanım örneği ve uygulama hakkında daha fazla tartışma yapmak istiyorum.
Sonuç olarak, kullanımı gerçekten kolay olduğu için pybind11'i şiddetle tavsiye ederim: sadece bir başlık dahil edersiniz ve sonra pybind11, Python'a maruz bırakmak istediğiniz C ++ sınıfını incelemek için şablon sihirli kullanır ve bunu şeffaf bir şekilde yapar.
Bu şablon sihrinin dezavantajı, pybind11 kullanan herhangi bir dosyaya derhal birkaç saniye ekleyerek derlemeyi yavaşlatmasıdır, örneğin , bu konuda yapılan soruşturmaya bakın . PyTorch da aynı fikirde .
İşte size pybind11'in ne kadar harika olduğuna dair bir fikir vermek için minimal bir çalıştırılabilir örnek:
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)
Derleyin ve çalıştırın:
#!/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
Bu örnek, pybind11'in ClassTest
C ++ sınıfını zahmetsizce Python'a maruz bırakmanıza nasıl izin verdiğini gösterir ! Derleme adlı bir dosya oluşturur otomatik olarak tanım noktası olarak alırclass_test.cpython-36m-x86_64-linux-gnu.so
class_test_main.py
class_test
yerel tanımlanan modül.
Belki de bunun ne kadar harika olduğunun farkına varmak, aynı şeyi yerel Python API ile elle yapmaya çalışırsanız, örneğin yaklaşık 10 kat daha fazla koda sahip olan bunu yapma örneğine bakın: https://github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.c Bu örnekte, C kodunun içerdiği tüm bilgilerle (üyeler, yöntemler, biraz daha acı verici ve açık bir şekilde nasıl tanımlanması gerektiğini görebilirsiniz) meta ...). Ayrıca bakınız:
pybind11 Boost.Python
, https://stackoverflow.com/a/145436/895245 adresinde belirtilene benzediğini iddia ediyor, ancak Boost projesinin içinde olmanın şişmesinden kurtulduğu için daha az:
pybind11, temel olarak mevcut C ++ kodunun Python bağlarını oluşturmak için Python'daki C ++ türlerini ve tam tersini gösteren hafif bir salt başlık kütüphanesidir. Hedefleri ve sözdizimi, David Abrahams'ın mükemmel Boost.Python kütüphanesine benziyor: derleme zamanı içgözlemini kullanarak tür bilgisini çıkartarak geleneksel genişletme modüllerindeki kazan plakasını en aza indirgemek.
Boost.Python ile ana sorun - ve benzer bir proje yaratmanın nedeni - Boost. Boost, var olan hemen hemen her C ++ derleyicisiyle çalışan çok büyük ve karmaşık bir yardımcı program kütüphanesidir. Bu uyumluluğun maliyeti vardır: en eski ve en ürkütücü derleyici örneklerini desteklemek için gizli şablon hileleri ve geçici çözümleri gerekir. C ++ 11 uyumlu derleyiciler yaygın olarak mevcut olduğundan, bu ağır makineler aşırı derecede büyük ve gereksiz bir bağımlılık haline gelmiştir.
Bu kütüphaneyi Boost'un kendi kendine yeten küçük bir sürümü olarak düşünün. Yorum yapılmadan, çekirdek başlık dosyaları yalnızca ~ 4K kod satırı gerektirir ve Python'a (2.7 veya 3.x veya PyPy2.7> = 5.7) ve C ++ standart kitaplığına bağlıdır. Bu kompakt uygulama, bazı yeni C ++ 11 dil özellikleri (özellikle: tuples, lambda fonksiyonları ve varyasyon şablonları) sayesinde mümkün oldu. Kuruluşundan bu yana, bu kütüphane Boost.Python'un birçok şekilde büyüdü ve birçok yaygın durumda bağlayıcı kodun daha basit olmasına yol açtı.
pybind11 aynı zamanda şu anki Microsoft Python C bağlama belgeleri tarafından şu adreste bulunan tek yerel olmayan alternatiftir: https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in- visual-studio? view = vs-2019 ( arşiv ).
Ubuntu 18.04, pybind11 2.0.1, Python 3.6.8, GCC 7.4.0 üzerinde test edilmiştir.