回答:
Boost.Pythonを見てください。ここに彼らのウェブサイトから取られた短い紹介があります:
Boost Python Libraryは、PythonとC ++をインターフェースするためのフレームワークです。これにより、特別なツールを使用せずにC ++コンパイラのみを使用して、C ++クラスの関数とオブジェクトをPythonにすばやくシームレスに公開できます。C ++インターフェイスを非侵入的にラップするように設計されているため、ラップするためにC ++コードをまったく変更する必要がなく、Boost.PythonはサードパーティのライブラリをPythonに公開するのに最適です。ライブラリで高度なメタプログラミング技術を使用すると、ユーザーの構文が簡略化されるため、ラッピングコードは一種の宣言型インターフェイス定義言語(IDL)のように見えます。
ctypesモジュールは標準ライブラリの一部であるため、常に問題を引き起こす傾向があったswigよりも安定しており、広く利用できます。
ctypesを使用すると、Pythonでのコンパイル時の依存関係をすべて満たす必要があり、バインディングは、コンパイルされたものだけでなく、ctypesを持つすべてのpythonで機能します。
foo.cppと呼ばれるファイルで、会話したい単純なC ++サンプルクラスがあるとします。
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
ctypeはC関数としか通信できないため、それらをextern "C"として宣言するものを提供する必要があります。
extern "C" {
Foo* Foo_new(){ return new Foo(); }
void Foo_bar(Foo* foo){ foo->bar(); }
}
次に、これを共有ライブラリにコンパイルする必要があります
g++ -c -fPIC foo.cpp -o foo.o
g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
そして最後に、Pythonラッパーを作成する必要があります(例: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)
それができたら、次のように呼び出すことができます
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
関数を提供し、それをpythonデストラクタから呼び出すか、オブジェクトをリソースにラップするなどして、後でポインタを削除することを忘れないでください。
これを行う最も速い方法はSWIGを使用することです。
SWIG チュートリアルの例:
/* File : example.c */
int fact(int n) {
if (n <= 1) return 1;
else return n*fact(n-1);
}
インターフェースファイル:
/* example.i */
%module example
%{
/* Put header files here or function declarations like below */
extern int fact(int n);
%}
extern int fact(int n);
UnixでPythonモジュールをビルドする:
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
使用法:
>>> import example
>>> example.fact(5)
120
python-devが必要であることに注意してください。また、一部のシステムでは、Pythonヘッダーファイルは、インストールした方法に基づいて/usr/include/python2.7にあります。
チュートリアルから:
SWIGはほぼすべての言語機能をサポートするかなり完全なC ++コンパイラです。これには、前処理、ポインター、クラス、継承、さらにはC ++テンプレートも含まれます。SWIGを使用して、構造とクラスをターゲット言語のプロキシクラスにパッケージ化することもできます—基本的な機能を非常に自然な方法で公開します。
私はこのページからPython <-> C ++バインディングへの旅を始めました。高レベルのデータ型(多次元STLベクトルとPythonリスト)をリンクすることを目的としています:-)
ctypesとboost.python(ソフトウェアエンジニアではない)の両方に基づいたソリューションを試してみたところ、高レベルのデータ型バインディングが必要な場合は複雑であることがわかりましたが、 SWIGにそのような場合のためにはるかにシンプルに。
したがって、この例ではSWIGを使用しており、Linuxでテストされています(ただし、SWIGは利用可能で、Windowsでも広く使用されています)。
目的は、2D STLベクトルの形式で行列を取得し、各行の平均を(1D STLベクトルとして)返すC ++関数をPythonで使用できるようにすることです。
C ++のコード( "code.cpp")は次のとおりです。
#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;
}
同等のヘッダー( "code.h")は次のとおりです。
#ifndef _code
#define _code
#include <vector>
std::vector<double> average (std::vector< std::vector<double> > i_matrix);
#endif
まず、C ++コードをコンパイルしてオブジェクトファイルを作成します。
g++ -c -fPIC code.cpp
次に、C ++関数用のSWIGインターフェイス定義ファイル( "code.i")を定義します。
%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を使用して、SWIGインターフェイス定義ファイルからC ++インターフェイスソースコードを生成します。
swig -c++ -python code.i
最後に、生成されたC ++インターフェイスソースファイルをコンパイルし、すべてをリンクして、Pythonで直接インポートできる共有ライブラリを生成します(「_」が重要です)。
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
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
またpybind11
、Boost.Pythonの軽量バージョンに似ており、すべての最新のC ++コンパイラと互換性があります。
Pytorch
pytorch.org/tutorials/advanced/cpp_extension.htmlなど、多くの巨大な関連プロジェクトが推奨しています。これは、VS Community
Windows でも完全に機能します
近代的なC ++の場合、使用cppyy: http://cppyy.readthedocs.io/en/latest/
Clang / LLVMのC ++インタープリターであるClingに基づいています。バインディングは実行時に行われ、追加の中間言語は必要ありません。Clangのおかげで、C ++ 17をサポートしています。
pipを使用してインストールします。
$ pip install cppyy
小規模なプロジェクトの場合は、関連するライブラリと興味のあるヘッダーをロードするだけです。たとえば、ctypesの例からコードを取得すると、このスレッドになりますが、ヘッダーセクションとコードセクションに分割されます。
$ cat foo.h
class Foo {
public:
void bar();
};
$ cat foo.cpp
#include "foo.h"
#include <iostream>
void Foo::bar() { std::cout << "Hello" << std::endl; }
コンパイルします。
$ g++ -c -fPIC foo.cpp -o foo.o
$ g++ -shared -Wl,-soname,libfoo.so -o libfoo.so foo.o
そしてそれを使う:
$ python
>>> import cppyy
>>> cppyy.include("foo.h")
>>> cppyy.load_library("foo")
>>> from cppyy.gbl import Foo
>>> f = Foo()
>>> f.bar()
Hello
>>>
大規模なプロジェクトは、準備されたリフレクション情報とそれらを作成するためのcmakeフラグメントの自動読み込みでサポートされているため、インストールされたパッケージのユーザーは簡単に実行できます。
$ python
>>> import cppyy
>>> f = cppyy.gbl.Foo()
>>> f.bar()
Hello
>>>
LLVMのおかげで、自動テンプレートインスタンス化などの高度な機能が可能です。例を続けるには:
>>> v = cppyy.gbl.std.vector[cppyy.gbl.Foo]()
>>> v.push_back(f)
>>> len(v)
1
>>> v[0].bar()
Hello
>>>
注:私はcppyyの作者です。
swig
、ctypes
またはboost.python
。PythonをC ++コードで動作させるためのコードを記述する必要はありません... PythonはC ++を理解するために大変な作業を行います。それが実際に機能すると仮定します。
Pythonがすべての科学者のニーズであると主張するこの論文は、基本的に次のように述べています。次に、パーツを高速化する必要がある場合は、SWIGを使用してこのパーツをCに変換します。
私はそれを使ったことがありませんが、ctypesについて良いことを聞いています。C ++で使用する場合は、を介して名前のマングリングを回避してくださいextern "C"
。コメントをありがとう、FlorianBösch。
Pythonのcffiはオプションになると思います。
目標は、PythonからCコードを呼び出すことです。あなたは第三言語を学ぶことなくそうすることができるはずです:すべての選択肢はあなた自身の言語(Cython、SWIG)またはAPI(ctypes)を学ぶことを要求します。そのため、PythonとCを知っていると想定し、学ぶ必要のあるAPIの追加ビットを最小限に抑えようとしました。
問題は、私が正しく理解した場合、PythonからC関数を呼び出す方法です。次に、最善の策はCtypes(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
公式のPythonドキュメントの1つに、C / C ++を使用したPythonの拡張に関する詳細が含まれています。SWIGを使用しなくても、非常に簡単で、Windowsで完全に機能します。
Cythonは、Javaラッパーを作成することを想定していない限り、間違いなく進むべき道です。その場合、SWIGが望ましいかもしれません。
私は runcython
コマンドラインユーティリティます。これにより、Cythonの使用プロセスが非常に簡単になります。構造化データをC ++に渡す必要がある場合は、Googleのprotobufライブラリをご覧ください。これは非常に便利です。
以下は、両方のツールを使用して作成した最小限の例です。
https://github.com/nicodjimenez/python2cpp
それが有用な出発点になることを願っています。
最初に、あなたはあなたの特定の目的が何であるかを決定する必要があります。Pythonインタープリターの拡張と埋め込みに関する公式のPythonドキュメントは上記のとおりです。バイナリ拡張の概要を追加できます。使用例は、次の3つのカテゴリに分類できます。
他の興味のある人に広い視野を与えるために、そしてあなたの最初の質問は少し曖昧なので(「CまたはC ++ライブラリにとって」)、この情報はあなたにとって興味深いかもしれません。上記のリンクで、バイナリ拡張とその代替を使用することの欠点について読むことができます。
提案されている他の回答とは別に、アクセラレータモジュールが必要な場合は、Numbaを試すことができます。「インポート時にLLVMコンパイラインフラストラクチャを使用して、または静的に(含まれているpyccツールを使用して)最適化されたマシンコードを生成することにより機能します。
私はcppyyが大好きです。C++コードでPythonを拡張するのが非常に簡単になり、必要に応じてパフォーマンスが劇的に向上します。
パワフルで、率直に言って非常に使いやすいです。
ここでは、numpy配列を作成して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;
}
};
また、非常に簡単に(CMakeを使用して)Pythonモジュールを作成できます。これにより、C ++コードを常に再コンパイルする必要がなくなります。
pybind11最小限の実行可能な例
pybind11は以前https://stackoverflow.com/a/38542539/895245で言及されていましたが、ここで具体的な使用例と実装に関するいくつかの詳細な説明を提供したいと思います。
pybind11は非常に使いやすいので、私はpybind11を強くお勧めします。ヘッダーを含めるだけで、pybind11はテンプレートマジックを使用して、Pythonに公開するC ++クラスを検査し、透過的に実行します。
このテンプレートマジックの欠点は、pybind11を使用するファイルに数秒追加するだけでコンパイルが遅くなることです。たとえば、この問題で行われた調査を参照してください。PyTorchも同意する。
以下は、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)
コンパイルして実行:
#!/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
この例は、pybind11を使用してClassTest
C ++クラスをPython に簡単に公開できる方法を示しています。コンパイルにより、の定義ポイントとして自動的に選択されるという名前のファイルが作成さclass_test.cpython-36m-x86_64-linux-gnu.so
れclass_test_main.py
ますclass_test
ネイティブに定義されたモジュールを。
恐らくこれがどれだけ素晴らしいかということの理解は、ネイティブPython APIを使用して同じことを手動で行おうとした場合にのみ発生します。たとえば、次の例のように、約10倍のコードが追加されています。https://github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.cこの例では、Cコードに含まれているすべての情報(メンバー、メソッド、さらにメタデータ...)。以下も参照してください。
pybind11はBoost.Python
、https: //stackoverflow.com/a/145436/895245 で言及されたものと同様であると主張していますが、Boostプロジェクト内にいるという膨張から解放されているため、より最小限に抑えられています。
pybind11は軽量のヘッダーのみのライブラリで、主に既存のC ++コードのPythonバインディングを作成するために、PythonでC ++型を公開します。その目的と構文は、David Abrahamsによる優れたBoost.Pythonライブラリに似ています。コンパイル時のイントロスペクションを使用して型情報を推測することにより、従来の拡張モジュールのボイラープレートコードを最小限に抑えます。
Boost.Pythonの主な問題、およびそのような類似のプロジェクトを作成する理由は、Boostです。Boostは、存在するほとんどすべてのC ++コンパイラで機能する、非常に大きく複雑なユーティリティライブラリスイートです。この互換性にはコストがかかります。最も古く、バグの多いコンパイラの見本をサポートするには、難解なテンプレートトリックと回避策が必要です。C ++ 11互換のコンパイラが広く利用できるようになった今、この重い機構は過度に大きく、不必要な依存関係になっています。
このライブラリは、バインディングの生成に関係のないすべてが取り除かれた、Boost.Pythonの小さな自己完結型バージョンと考えてください。コメントがない場合、コアヘッダーファイルは〜4K行のコードのみを必要とし、Python(2.7または3.x、またはPyPy2.7> = 5.7)およびC ++標準ライブラリに依存します。このコンパクトな実装は、いくつかの新しいC ++ 11言語機能(具体的には、タプル、ラムダ関数、可変テンプレート)のおかげで可能になりました。作成以来、このライブラリはさまざまな方法でBoost.Pythonを超えて成長しており、多くの一般的な状況で劇的に単純なバインディングコードにつながっています。
pybind11は、現在のMicrosoft Python Cバインディングドキュメントで強調表示されている唯一の非ネイティブな代替でもあります。https://docs.microsoft.com/en-us/visualstudio/python/working-with-c-cpp-python-in- visual-studio?view = vs-2019(アーカイブ)。
Ubuntu 18.04、pybind11 2.0.1、Python 3.6.8、GCC 7.4.0でテスト済み。