web-dev-qa-db-ja.com

PythonからC/C++を呼び出す場合

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

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

459
shoosh

Boost.Python をご覧ください。これが彼らのウェブサイトから取られた短い紹介です:

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

148
Ralph

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
582
Florian Bösch

これを行う最も簡単な方法は _ 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はまた、ターゲット言語で構造体やクラスをプロキシクラスにパッケージ化するためにも使用できます。つまり、基盤となる機能を非常に自然な方法で公開します。

51
Ben Hoffstein

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

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

pyrex または Cython をチェックしてください。それらはC/C++とPythonの間のインターフェースのためのPython風の言語です。

27
Jason Baker

pybind11もあります。これは Boost.Python の軽量版のようなもので、最近のすべてのC++コンパイラと互換性があります。

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

20
Tom Wenseleers

この論文は、Pythonがすべての科学者のニーズであると主張しています 、基本的には次のように述べています。最初にすべてをPythonでプロトタイプ化します。それからあなたが部分をスピードアップする必要があるとき、SWIGを使って、この部分をCに翻訳してください.

18
Yuval F

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

15
John

現代の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の作者です。

14
Wim Lavrijsen

私は、Python用のcffiが選択肢になると思います。

目標はPythonからCコードを呼び出すことです。あなたは第三言語を学ぶことなくそうすることができるはずです:あらゆる代替はあなた自身の言語(Cython、SWIG)またはAPI(ctypes)を学ぶことを要求します。ですから、私たちはあなたがPythonとCを知っていると仮定し、あなたが学ぶ必要があるAPIの余分な部分を最小限に抑えるようにしました。

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

11
mrgloom

公式のPythonドキュメントの1つに、 C/C++ を使用したPythonの拡張に関する詳細が記載されています。 _ swig _ を使用しなくても、非常に簡単で、Windowsでも完璧に機能します。

7

問題は、私が正しく理解していれば、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

詳細なガイドについては、 私のブログ記事 を参照してください。

5
Jadav Bheda

最初にあなたはあなたの特定の目的が何であるかを決めるべきです。 Pythonインタプリタの拡張と埋め込み に関する公式のPythonドキュメントは上で述べられています、私は良い バイナリ拡張の概要 を加えることができます。ユースケースは3つのカテゴリに分けられます。

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

他の関心を持つ人に対してより広い視野を与えるために、そしてあなたの最初の質問は少しあいまいなので( "CやC++ライブラリに")私はこの情報があなたにとって興味深いかもしれないと思います。上のリンクで、バイナリ拡張とその代替手段を使うことの不利な点を読むことができます。

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

5

Javaラッパーを書くことを考えていない限り、Cythonは間違いなく有効な方法です。その場合はSWIGが推奨されます。

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

これは、両方のツールを使用した私が作成した最小限の例です。

https://github.com/nicodjimenez/python2cpp

それが役に立つ出発点になることを願っています。

5
nicodjimenez

Scapix Language Bridge を使用して、ビルドの一部としてC++ヘッダーから直接Pythonバインディングを自動的に生成できます。

scapix_bridge_headers()への呼び出しをCMakeLists.txtファイルに追加し、cmake -DSCAPIX_BRIDGE=pythonを使用してプロジェクトをビルドします。完全な を参照してください。

免責事項:私は Scapix Language Bridge の著者です。

0
Boris Rasin