CまたはC++ライブラリへのPythonバインディングを構築するための最も早い方法は何でしょうか。
(これが問題になる場合は、私はWindowsを使用しています。)
Boost.Python をご覧ください。これが彼らのウェブサイトから取られた短い紹介です:
Boost Python Libraryは、PythonとC++を連携させるためのフレームワークです。特別なツールを使用せずに、C++コンパイラだけで、C++クラスの関数やオブジェクトをPythonにすばやくシームレスに公開できます。 Boost.Pythonは、C++インターフェイスを邪魔にならないようにラップするように設計されているため、ラップするためにC++コードをまったく変更する必要はなく、サードパーティのライブラリをPythonに公開するのに最適です。ライブラリが高度なメタプログラミング技術を使用すると、ユーザーにとって構文が単純化されるため、ラッピングコードは一種の宣言型インターフェイス定義言語(IDL)の外観を帯びます。
ctypes は標準ライブラリの一部なので、 swig よりずっと安定で広く利用可能です 問題 .
Ctypesでは、コンパイル時のpythonへの依存関係をすべて満たす必要があります。バインディングは、ctypesを持つPythonだけでなく、それに対してコンパイルされたものでも動作します。
Foo.cppという名前のファイルに、会話したい単純なC++のサンプルクラスがあるとします。
#include <iostream>
class Foo{
public:
void bar(){
std::cout << "Hello" << std::endl;
}
};
Ctypesは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
これを行う最も簡単な方法は _ 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++バインディングへの旅を始めました。高水準データ型(Pythonのリストを持つ多次元STLベクトル)をリンクする目的で:-)
ctypes と boost.python (そしてソフトウェアエンジニアではない)の両方に基づいた解決策を試した結果、高レベルのデータ型バインディングが必要な場合は複雑になりましたそのような場合は SWIG はるかに単純です。
したがって、この例ではSWIGを使用しています。Linuxでテストされています(ただし、SWIGは利用可能で、Windowsでも広く使用されています)。
目的は、2DのSTLベクトルの形式で行列を取得し、各行の平均を(1DのSTLベクトルとして)返すPythonでC++関数を使用できるようにすることです。
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++コンパイラと互換性があります。
この論文は、Pythonがすべての科学者のニーズであると主張しています 、基本的には次のように述べています。最初にすべてをPythonでプロトタイプ化します。それからあなたが部分をスピードアップする必要があるとき、SWIGを使って、この部分をCに翻訳してください.
私はこれを使ったことは一度もありませんが、 ctypes について良いことを聞いたことがあります。 C++で使用しようとしている場合は、 extern "C"
を使用して名前の変換を回避してください。 コメントをありがとう、FlorianBösch。
現代のC++の場合は、cppyyを使用します。 http://cppyy.readthedocs.io/en/latest/
それはCling、Clang/LLVMのためのC++インタプリタに基づいています。バインディングは実行時にあり、追加の中間言語は必要ありません。 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の作者です。
私は、Python用のcffiが選択肢になると思います。
目標はPythonからCコードを呼び出すことです。あなたは第三言語を学ぶことなくそうすることができるはずです:あらゆる代替はあなた自身の言語(Cython、SWIG)またはAPI(ctypes)を学ぶことを要求します。ですから、私たちはあなたがPythonとCを知っていると仮定し、あなたが学ぶ必要があるAPIの余分な部分を最小限に抑えるようにしました。
問題は、私が正しく理解していれば、PythonからC関数を呼び出す方法です。それなら最善の策はCtypesです(BTWは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インタプリタの拡張と埋め込み に関する公式のPythonドキュメントは上で述べられています、私は良い バイナリ拡張の概要 を加えることができます。ユースケースは3つのカテゴリに分けられます。
他の関心を持つ人に対してより広い視野を与えるために、そしてあなたの最初の質問は少しあいまいなので( "CやC++ライブラリに")私はこの情報があなたにとって興味深いかもしれないと思います。上のリンクで、バイナリ拡張とその代替手段を使うことの不利な点を読むことができます。
他の提案された答えとは別に、アクセラレータモジュールが欲しいなら、 Numba を試すことができます。これは、「インポート時、実行時、または静的に(付属のpyccツールを使用して)LLVMコンパイラインフラストラクチャを使用して最適化されたマシンコードを生成することで」機能します。
Javaラッパーを書くことを考えていない限り、Cythonは間違いなく有効な方法です。その場合はSWIGが推奨されます。
runcython
コマンドラインユーティリティを使用することをお勧めします。Cythonを使用するプロセスが非常に簡単になります。構造化データをC++に渡す必要がある場合は、Googleのprotobufライブラリを見てください。とても便利です。
これは、両方のツールを使用した私が作成した最小限の例です。
https://github.com/nicodjimenez/python2cpp
それが役に立つ出発点になることを願っています。
Scapix Language Bridge を使用して、ビルドの一部としてC++ヘッダーから直接Pythonバインディングを自動的に生成できます。
scapix_bridge_headers()
への呼び出しをCMakeLists.txtファイルに追加し、cmake -DSCAPIX_BRIDGE=python
を使用してプロジェクトをビルドします。完全な 例 を参照してください。
免責事項:私は Scapix Language Bridge の著者です。