ホームページ  >  記事  >  バックエンド開発  >  Python と C 間での相互呼び出しの詳細な紹介

Python と C 間での相互呼び出しの詳細な紹介

零下一度
零下一度オリジナル
2017-07-16 12:03:481717ブラウズ

Pythonは開発効率は高いものの、スクリプト言語としての性能は高くないため、開発効率やパフォーマンスを考慮するため、性能要件の高いモジュールはCまたはC++で実装するか、CでPythonスクリプトを実行することが一般的です。処理にはロジックまたは C++ が使用されます。前者は通常、Python でのいくつかのモジュールの実装であり、後者はサーバーサイド プログラム (ビジネス拡張またはプラグイン機能の実装) やゲーム開発 (スクリプトはロジックのみを処理します) でより一般的です。この記事では、cでpythonスクリプトを実行することでpythonとcの相互呼び出しを実現する方法を主に、cとpythonスクリプトで同じメモリ領域を設定する例を用いて紹介します。

はじめに

最近、仕事の都合でオンラインゲーム対戦用のudpベースのデータ同期プロトコルを作成しようと考えており、初期段階のデータをテストするために外部プロキシを作成することにしました。原則はサーバー側で、クライアントとは別にネットワーク転送プロキシを確立します。つまり、元の C/S 接続が 2 つのプロキシ間の高速データ送信に変更されます。 udp ライブラリは C++ で書かれたコードであるため、データをテストするときにパラメータを変更したり、再コンパイルしたり、出力統計データを変更したり、表を作成したりするのは非常に面倒です。最終的に、エクスポート インターフェイスへの論理呼び出しを行うために Python スクリプトを使用することにしました。以下で言うことはあまりありませんが、詳細な紹介を見てみましょう:

準備作業

c で Python スクリプトを実行するには、プログラムがリンクされるときに Python 仮想マシン ライブラリをそれにリンクする必要があります。 Python 仮想マシンのライブラリは、インストール ディレクトリの libs にある python27.lib ファイルです。ライブラリをプログラムにリンクする方法については、自分で検索できます。 Python の一部のメソッドとデータ構造は C で使用されているため、Python インストール ディレクトリ下の include ディレクトリをプロジェクトのインクルード ディレクトリに追加する必要があります。準備はこれだけです。これで、メモリ領域の設定例の実装を開始できます。

C/C++ を Python にエクスポートするには、さまざまなニーズに応じて、次のさまざまな方法を使用できます。

1. ctypes は、ユニバーサル Python 標準ライブラリ モジュールに含まれており、実行時にダイナミック リンク ライブラリ (dll など) をロードでき、CPython 2.x/3.x および PyPy でサポートされています。この方法の利点は、Python API を使用してエクスポート 関数 を記述する必要がなく、ダイナミック リンク ライブラリのシンボル テーブルを直接ロードして、Python で直接呼び出すことができることです。

2. サードパーティの Python バインディング。例としては、boost-python が挙げられます。これは、Python/C API を使用してツール自動化によって実装され、一連の C++ ラッパー関数を生成します。大規模なライブラリやエンジンを Python にエクスポートする場合に特に適しています。

3. Python バインディング関数を手動で作成します。 Python C API に精通している場合は、この方法が最も柔軟であり、API ドキュメントを読んだ後に使用できます。理論上は効率が最高になるはずですが、私のようなPython初心者にとってはかなりの時間がかかるかもしれません。

C 関数を Lua スクリプトにエクスポートした以前の経験に基づいて、最初に Python C API を勉強し、それを完了するまでに半日取り組む必要があると思いました。後で、Python 標準ライブラリ モジュールの ctypes がすでに非常に強力であることがわかりました。パフォーマンスは 3 つの方法の中で最も悪いはずですが、最大 60fps のこのトンネルでは、C/Python インターフェイス境界呼び出しの損失は無視されます。初めて。他の 2 つの設計方法とは異なり、ctypes はインターフェイスを呼び出す非侵襲的な方法を使用します。元の C インターフェイスを変更したり、バインディング コードを作成したりする必要はなく、コンパイルされた動的ライブラリを直接呼び出します。 ctypes を使用するプロセスも非常に快適です。

以下では ctypes の使用方法を紹介します:

1. DLL ダイナミック リンク ライブラリをロードします

ここでは、ダイナミック リンク ライブラリ関数が cdecl 呼び出しを使用するか stdcall 呼び出しを使用するかを区別する必要があります。規則に従って、それぞれ cdll または Windll を使用して動的ライブラリをロードします。

例:

# 加载udp库函数 
udp_server = cdll.LoadLibrary("./udp_server.so") 
init_udp_server = udp_server.init_udp_server 
destroy_udp_server = udp_server.destroy_udp_server 
update_udp_server = udp_server.update_udp_server 
SendMsg = udp_server.SendMsg 

SetConnectCallback = udp_server.SetConnectCallback 
SetDisconnectCallback = udp_server.SetDisconnectCallback 
SetTimeoutCallback = udp_server.SetTimeoutCallback 
SetRecvCallback = udp_server.SetRecvCallback

2、データ型マッピング

ctypes で定義される基本的なデータ型 (c_char、c_int、c_double など) に加えて、ポインター関数を使用して次のこともできます。ポインタ型に変換します。ネットワーク ライブラリをエクスポートするには、コールバック関数を設定することが必須です。C++ ライブラリでは、コールバック関数は、関数ポインター の宣言もサポートしています。例: recv_cb = CFUNCTYPE( None, c_char_p, c_int ) は、戻り値が void で、パラメータが char* および int 型であるコールバック関数を示します。


def init(self, port, ip="127.0.0.1"): 
  self._port = port 
  self._ip = ip 

  self._clients = {} 

  self.c_connect_cb = connect_cb(self.server_connect) 
  self.c_disconnect_cb = disconnect_cb(self.server_disconnect) 
  self.c_timeout_cb = timeout_cb(self.server_timeout) 
  self.c_recv_cb = recv_cb(self.server_recv) 

def create(self): 
  if self._port: 
   if init_udp_server(self._ip, self._port) == 0: 
    print "server listen %s:%d" % (self._ip, self._port) 
    SetConnectCallback( self.c_connect_cb ) 
    SetDisconnectCallback( self.c_disconnect_cb ) 
    SetTimeoutCallback( self.c_timeout_cb ) 
    SetRecvCallback( self.c_recv_cb ) 
    return True 
  print "[error] init_udp_server error", self._ip, self._port 
  return False

コールバックパラメータをバインドするときは、Pythonのガベージコレクションによってコールバック関数がワイルドポインタになるのを避けるために、バインドされたコールバック関数をメンバー変数として保存する必要があることに注意する必要があります(上記の記述方法)。 。これは小さな穴であると考えられます。基本的に、小規模なライブラリではこれらの関数のみが使用されます。

以上がPython と C 間での相互呼び出しの詳細な紹介の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。