PythonからC / C ++を呼び出す?


521

CまたはC ++ライブラリへのPythonバインディングを構築する最も速い方法は何でしょうか?

(これが問題になる場合は、Windowsを使用しています。)

回答:


170

Boost.Pythonを見てください。ここに彼らのウェブサイトから取られた短い紹介があります:

Boost Python Libraryは、PythonとC ++をインターフェースするためのフレームワークです。これにより、特別なツールを使用せずにC ++コンパイラのみを使用して、C ++クラスの関数とオブジェクトをPythonにすばやくシームレスに公開できます。C ++インターフェイスを非侵入的にラップするように設計されているため、ラップするためにC ++コードをまったく変更する必要がなく、Boost.PythonはサードパーティのライブラリをPythonに公開するのに最適です。ライブラリで高度なメタプログラミング技術を使用すると、ユーザーの構文が簡略化されるため、ラッピングコードは一種の宣言型インターフェイス定義言語(IDL)のように見えます。


Boost.Pythonは、Boostのユーザーフレンドリーなライブラリの1つです。単純な関数呼び出しAPIの場合、これは非常に簡単で、自分で書く必要があるボイラープレートを提供します。オブジェクト指向のAPIを公開する場合は、少し複雑になります。
jwfearn 2008

15
Boost.Pythonは想像できる最悪のものです。すべての新しいマシンとすべてのアップグレードで、リンクの問題が発生します。
ミラー、

14
11年近く後、この答えの質について熟考する時が来ましたか?
Jエヴァンス

4
これはまだpythonとc ++のインターフェースへの最良のアプローチですか?
tushaR

8
ブーストに比べて軽量なpybind11を試してみてください。
jdhao

659

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

14
これは、boost.pythonが単一の関数呼び出しで行うこととほぼ同じです。
マーティンベケット

203
ctypesはpython標準ライブラリにありますが、swigとboostはありません。Swigとboostは拡張モジュールに依存しているため、独立した共有オブジェクトではないpythonマイナーバージョンに関連付けられています。swigやboostラッパーをビルドするのは面倒な場合があります。ctypesを使用するとビルドの要件がなくなります。
FlorianBösch09年

25
boostは、ブードゥーテンプレートの魔法と完全にカスタムビルドシステムに依存し、ctypesはシンプルさに依存しています。ctypesは動的で、boostは静的です。ctypesは、さまざまなバージョンのライブラリを処理できます。ブーストはできません。
フロリアンベッシュ

32
Windowsでは、Pythonがそれらを表示できるように、関数のシグネチャで__declspec(dllexport)を指定する必要がありました。上記の例から、これはに対応する extern "C" { __declspec(dllexport) Foo* Foo_new(){ return new Foo(); } __declspec(dllexport) void Foo_bar(Foo* foo){ foo->bar(); } }
アラン・マクドナルド

13
後でFoo_delete関数を提供し、それをpythonデストラクタから呼び出すか、オブジェクトをリソースにラップするなどして、後でポインタを削除することを忘れないでください。
Adversus、2015年

58

これを行う最も速い方法は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を使用して、構造とクラスをターゲット言語のプロキシクラスにパッケージ化することもできます—基本的な機能を非常に自然な方法で公開します。


50

私はこのページからPython <-> C ++バインディングへの旅を始めました。高レベルのデータ型(多次元STLベクトルとPythonリスト)をリンクすることを目的としています:-)

ctypesboost.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

C ++コードでstlベクトルが非const参照として渡されるため、Pythonによって出力パラメーターとして使用できる実際の実装:lobianco.org/antonello/personal
Antonello

33

またpybind11Boost.Pythonの軽量バージョンに似ており、すべての最新のC ++コンパイラと互換性があります。

https://pybind11.readthedocs.io/en/latest/


1
今日 !! 2020これが一番の答えになるはずです!テンプレートヘッダーのみのライブラリです。Pytorch pytorch.org/tutorials/advanced/cpp_extension.htmlなど、多くの巨大な関連プロジェクトが推奨しています。これは、VS CommunityWindows でも完全に機能します
eusoubrasileiro

30

pyrexまたはCythonをチェックしてください。これらは、C / C ++とPythonの間のインターフェイスを提供するPythonのような言語です。


1
Cythonの+1!私はcffiを試したことがないのでどちらが良いかはわかりませんが、Cythonで非常に良い経験をしました-あなたはまだPythonコードを書いていますが、その中でCを使うことができます。:私は後でブログの記事で説明しCython、とビルドプロセスを設定することがやや困難だったmartinsosic.com/development/2016/02/08/...
Martinsos

リンクのみの回答ではなくなるように回答を改善したい場合があります。
アデリン

私は約1週間Cythonを使用していて、とても気に入っています。1)ctypesが使用されているのを見ましたが、醜く、非常にエラーが発生しやすく、多くの落とし穴があります。2)Pythonコードを使用して高速化できます静的にタイプするだけで3)C / C ++メソッドおよびオブジェクト用のPythonラッパーを記述するのは簡単です4)まだ十分にサポートされています。venvsへのインストールとクロスコンパイルについてのガイダンスでそれを行うことができます。ここには非常に優れた4時間のビデオチュートリアルがあります。youtube.com
=

22

近代的な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の作者です。


3
そうではありません。Cythonは、Pythonに似たプログラミング言語で、Python用のC拡張モジュールを作成します(Cythonコードは、必要なC-APIボイラープレートとともにCに変換されます)。いくつかの基本的なC ++サポートを提供します。cppyyを使用したプログラミングには、PythonおよびC ++のみが含まれ、言語拡張は含まれません。完全にランタイムであり、オフラインコードを生成しません(遅延生成ははるかに優れています)。これは最新のC ++(自動テンプレートのインスタンス化、移動、initializer_lists、ラムダなどを含む)をターゲットにしており、PyPyはネイティブで(つまり、低速のC-APIエミュレーションレイヤーではなく)サポートされています。
Wim Lavrijsen

2
このPyHPC'1​​6ペーパーには、さまざまなベンチマーク番号が含まれています。それ以来、CPython側では確実な改善が行われています。
Wim Lavrijsen

あなたが追加の統合作業を行う必要はありませんので、私は、このアプローチが好きswigctypesまたはboost.python。PythonをC ++コードで動作させるためのコードを記述する必要はありません... PythonはC ++を理解するために大変な作業を行います。それが実際に機能すると仮定します。
Trevor Boyd Smith

cppyyは非常に興味深いです。再配布と事前パッケージ化が処理されることがドキュメントでわかります。これは、Pythonコードをパッケージ化するツール(PyInstallerなど)でもうまく機能することがわかっていますか?そしてこれはROOTプロジェクトに関連していますか、それともその作業を活用していますか?
JimB

ありがとう!私はPyInstallerに詳しくありませんが、前方宣言、パス、ヘッダーをパッケージ化した「辞書」は、共有ライブラリにコンパイルされたC ++コードです。cppyyはC ++コードのバインドに使用されるので、さらにいくつかのC ++コードの処理で問題ないと思います。そして、そのコードはPython C-APIに依存していません(libcppyyモジュールのみが依存しています)。cppyy自体はconda-forgeまたはpypi(pip)からインストールできるため、これらの環境はすべて確実に機能します。はい、cppyyはPyROOTからフォークとして始まりましたが、それ以降、ROOTチームはPyROOTをcppyyの上にリベースするように大幅に改善されました。
Wim Lavrijsen


15

私はそれを使ったことがありませんが、ctypesについて良いことを聞いています。C ++で使用する場合は、を介して名前のマングリングを回避してくださいextern "C"コメントをありがとう、FlorianBösch。


13

Pythonのcffiはオプションになると思います。

目標は、PythonからCコードを呼び出すことです。あなたは第三言語を学ぶことなくそうすることができるはずです:すべての選択肢はあなた自身の言語(Cython、SWIG)またはAPI(ctypes)を学ぶことを要求します。そのため、PythonとCを知っていると想定し、学ぶ必要のあるAPIの追加ビットを最小限に抑えようとしました。

http://cffi.readthedocs.org/en/release-0.7/


2
これはc(c ++ではなく)しか呼び出せず、+ 1を呼び出すことができると思います(私はcffiが本当に好きです)。
Andy Hayden

8

問題は、私が正しく理解した場合、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

詳細なガイドについては、私のブログ記事を参照することを勧めします。


ctypesは移植可能ですが、コードにはWindows固有のCライブラリが必要です。
Palec、2015


6

Cythonは、Javaラッパーを作成することを想定していない限り、間違いなく進むべき道です。その場合、SWIGが望ましいかもしれません。

私は runcythonコマンドラインユーティリティます。これにより、Cythonの使用プロセスが非常に簡単になります。構造化データをC ++に渡す必要がある場合は、Googleのprotobufライブラリをご覧ください。これは非常に便利です。

以下は、両方のツールを使用して作成した最小限の例です。

https://github.com/nicodjimenez/python2cpp

それが有用な出発点になることを願っています。


5

最初に、あなたはあなたの特定の目的が何であるかを決定する必要があります。Pythonインタープリターの拡張と埋め込みに関する公式のPythonドキュメントは上記のとおりです。バイナリ拡張の概要を追加できます。使用例は、次の3つのカテゴリに分類できます。

  • アクセラレータモジュール:CPythonで実行される同等の純粋なPythonコードよりも高速に実行します。
  • ラッパーモジュール:既存のCインターフェイスをPythonコードに公開します。
  • 低レベルのシステムアクセス:CPythonランタイム、オペレーティングシステム、または基盤となるハードウェアの低レベルの機能にアクセスします。

他の興味のある人に広い視野を与えるために、そしてあなたの最初の質問は少し曖昧なので(「CまたはC ++ライブラリにとって」)、この情報はあなたにとって興味深いかもしれません。上記のリンクで、バイナリ拡張とその代替を使用することの欠点について読むことができます。

提案されている他の回答とは別に、アクセラレータモジュールが必要な場合は、Numbaを試すことができます。「インポート時にLLVMコンパイラインフラストラクチャを使用して、または静的に(含まれているpyccツールを使用して)最適化されたマシンコードを生成することにより機能します。


3

私は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 ++コードを常に再コンパイルする必要がなくなります。


2

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を使用してClassTestC ++クラスをPython に簡単に公開できる方法を示しています。コンパイルにより、の定義ポイントとして自動的に選択されるという名前のファイルが作成さclass_test.cpython-36m-x86_64-linux-gnu.soclass_test_main.pyますclass_testネイティブに定義されたモジュールを。

恐らくこれがどれだけ素晴らしいかということの理解は、ネイティブPython APIを使用して同じことを手動で行おうとした場合にのみ発生します。たとえば、次の例のように、約10倍のコードが追加されています。https//github.com /cirosantilli/python-cheat/blob/4f676f62e87810582ad53b2fb426b74eae52aad5/py_from_c/pure.cこの例では、Cコードに含まれているすべての情報(メンバー、メソッド、さらにメタデータ...)。以下も参照してください。

pybind11はBoost.Pythonhttps: //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でテスト済み。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.