Linux で boost.python を使用して C++ 動的ライブラリを呼び出す方法

Linux で boost.python を使用して C++ 動的ライブラリを呼び出す方法

序文

最近、C++ 動的ライブラリをテストするためにロボット フレームワークを使い始めました。ロボット フレームワークは Windows 上で実行され、C++ 動的ライブラリはリモート Linux ホスト上で実行されます。テスト方法は、ロボット フレームワークに SSHLIbrary ライブラリを介してリモート マシン上の Python スクリプトを実行させ、Python スクリプトが C++ 動的ライブラリを呼び出すことです。そこで、Python で C++ 動的ライブラリを呼び出す方法という問題を解決する必要があります。

Python で C++ 動的ライブラリを呼び出す 2 つの方法

オンラインで検索し、同僚に相談した結果、2 つの方法を見つけました。1 つ目は、C++ 動的ライブラリを C インターフェイスにカプセル化し、Python に C 言語インターフェイスを呼び出させる方法です。 Python は C インターフェイスのみを呼び出すことができ、C++ インターフェイスを直接呼び出すことはできないため、カプセル化のレイヤーが必要です。カプセル化方法: extern "C" 宣言方法を使用して、C++ インターフェイスの上に C 言語インターフェイスのレイヤーをカプセル化します。この方法を試したところ、純粋な C 呼び出しは実行可能だが、Python 呼び出しは実行不可能であることがわかりました。その理由については以下で詳しく説明します。 2 番目の方法は、C++ boost ライブラリを使用して、Python が呼び出すインターフェイスを生成することです。これはテスト済みで実行可能ですが、プロセスは非常に複雑です。以下では、発生した問題とその解決策について詳しく説明します。

extern "C" の性質を理解する

最初の方法を説明する前に、extern "C" メソッドの機能を簡単に紹介しましょう。具体的な説明については、こちらのブログが非常に詳しく書かれているので一読をお勧めします。例えばC言語では、関数

int を追加します(int a,int b);

gcc コンパイラを使用する場合、コンパイルによって生成される名前は add ですが、g++ コンパイラを使用する場合、コンパイルによって生成される名前は、関数名、入力パラメータの数、型、戻り値を含む ABaddCD のようなものになることがあります。したがって、C++でもオーバーロードされた

フロートを追加します(フロートa、フロートb);

コンパイルによって生成される名前は EFaddGH のようなもので、関数名、入力パラメータ、戻り値などの情報も含まれているため、C++ をオーバーロードできます。 gcc コンパイラを使用すると、すべてが add と呼ばれ、どの関数であるかがわからないため、オーバーロードできないことを想像してください。そして、extern "C" の役割は、g++ コンパイラに、int add(int a, int b) を ABaddCD ではなく add にコンパイルするように指示することです。これは、add は C 言語で認識できますが、ABaddCD は C 言語で認識できず、C 言語は add を「未定義のシンボル」と認識するためです。したがって、ここから、extern "C" は C++ コードにのみ使用できることもわかります。さらに、オーバーロードされた C++ 関数の場合、名前が重複しないように、2 つの異なる関数を記述して個別に呼び出す必要があります。

Python は extern "C" を使用して C++ 動的ライブラリを呼び出します。

extern "C"の性質を理解した後、この方法に従ってカプセル化します。 C++ 動的ライブラリのソース コードを直接取得し、ソース コードの上に C インターフェイスのレイヤーをカプセル化して、動的ライブラリを生成しました。 add 関数が addc としてカプセル化され、C++ 動的ライブラリが A と呼ばれ、C インターフェイスのレイヤーをカプセル化した後に生成された動的ライブラリが B と呼ばれるとします。 test.c という名前のテスト コードを記述し、純粋な C コードを使用して動的ライブラリ B をチェックし、addc 関数を呼び出すと、結果は実行可能で成功します。ただし、Python を使用して動的ライブラリ B をチェックし、addc 関数を呼び出すと、次のエラーが報告されます。

AttributeError: B.so: 未定義のシンボル: 追加

つまり、追加機能はまだ認識されません。使用

nm B.so | grep 追加

取得できる

追加
ABaddCD

その結果、最初の addc は Python によって確実に認識されますが、2 番目の ABaddCD は g++ コンパイルによって生成された名前であり、Python では呼び出すことができません。ここでは、私自身の経験からの例を挙げています。私が作成した C++ 動的ライブラリのソース コードは比較的複雑で、Python で正常に呼び出すことができない場合があります。正常に呼び出すことができる例は、オンライン上に多数あります。読者の皆さんは自分で実験してみてください。うまくコールできれば最高です。なぜなら、次に紹介する boost.python の使い方がかなり複雑だからです。

Pythonはboost.pythonを使用してC++動的ライブラリを呼び出します

C++ 動的ライブラリが依存する他のサードパーティライブラリを解決する

私の動的ライブラリは、openssl、uuid、libevent、pthread などの他のサードパーティ ライブラリ ファイルに依存しているため、C++ 動的ライブラリを呼び出すためにどのメソッドが使用されるかに関係なく、これらの動的ライブラリをロードするには Python が必要です。具体的な Python コードは次のとおりです。

ctypes インポートから *
ctypes.CDLL("libssl.so", モード=ctypes.RTLD_GLOBAL) 
ctypes.CDLL("libcrypto.so", モード=ctypes.RTLD_GLOBAL) 
ctypes.CDLL("libuuid.so", モード=ctypes.RTLD_GLOBAL) 
ctypes.CDLL("/usr/lib64/libevent.so", モード=ctypes.RTLD_GLOBAL) 
#ctypes.CDLL("/usr/lib64/libpthread.so.0", モード=ctypes.RTLD_GLOBAL)

libpthread.so など、デフォルトでロードできるものもありますが、これらはロードする必要はありません。libssl.so や libuuid.so など、手動でロードする必要があるものもありますが、これらはすべて /usr/lib64/ ディレクトリにあり、パスに追加する必要はありません。ただし、libevent ライブラリは /usr/lib64 ディレクトリにもあり、/usr/lib/ ディレクトリにもあるため、パスを追加する必要があります。したがって、コンパイルが失敗した場合は、whereis libevent.so を使用して、どのディレクトリにあるかを確認し、絶対パスを追加します。絶対パスを追加しても機能しない場合があります。たとえば、libpthread.so は絶対パスを追加した後もエラーを報告します。

'OSError: /usr/lib64/libpthread.so: 無効な ELF ヘッダー'

これはバージョン番号が間違っていることを意味します。libpthread.so リンクのバージョン番号を見つけて、.0 バージョン番号を追加すると、エラーは報告されなくなります。

C++ コード構成ブースト環境

C++ ダイナミック ライブラリが配置されている Centos6.6 マシンでは、Ubuntu Python が C/C++ メソッドのダイナミック リンク ライブラリ構成を呼び出し、ブーストをテストするを参照してください。参考: Boost.Python を使用して Python C/C++ 混合プログラミングを実装し、Python 定義の C++ 関数オーバーロードを実装します。環境構築時に使用したコマンドは、yum install boost*、yum install python-develです。この2つの記事を参考にboostを実装しましたが、基本的にすべてパスしました。遭遇した問題もこの2つの記事に記載されていました。他にも問題に遭遇しましたが、Stack Overflow で解決策を見つけました。結果を下記に投稿します。

新しい test.cpp を作成します。ここで、Python で使用できる関数を定義する必要があります。

test.cpp コードに次のコードを含めます。

// boost ヘッダーファイルをインクルードする必要があります #include <boost/python.hpp> 
#include <boost/python/module.hpp> 
#include <boost/python/def.hpp>

//オーバーロードされた関数の実装。私の C++ コードでは、LOGIN 関数、Synchronize_Request 関数、Notify 関数のすべてに 3 つのオーバーロードされた関数があります。以下では、1 つの LOGIN 関数、1 つの Synchronize_Request 関数、および 2 つの Notification 関数のみを使用します。たとえば、以下の fun3 と fun4 は 2 つの異なる通知関数です。

// オーバーロードされた関数のみ、このように fun1、fun2、fun3、fun4 を定義する必要があります。オーバーロードされた関数がない場合は、名前 int (*fun1)(const int server_type, const int request_no, std::string& login_result) = &LOGIN; を直接記述できます。

int (*fun2)(const int server_type, const int request_no,std::string& recv_answer) = &Synchronize_Request; 
int (*fun3)(const int server_type, unsigned int timeout_ms, unsigned int sesscare) = &Notify;

int (*fun4)(void) = &Notify;

// 関数オーバーロードの例を追加します int (*fun5)(int a,int b) = &add;

 
BOOST_PYTHON_MODULE( libB ) //pythonモジュール、libBの名前は.soの名前と一致している必要があります{ 
 名前空間 boost::python を使用します。

 //Initialize関数はオーバーロードされていないので、上記のようにfun1を定義せずに直接使用できます。 
 def("初期化",初期化);
 //Uninitialize 関数はオーバーロードされていないので、直接使用します def("Uninitialize",Uninitialize); 
 def("ログイン",fun1); 
 def("Synchronize_Request",fun2); 
 def("通知",fun3); 
 def("Notify2",fun4); 
 def("add",fun5); 
 // Python は上記の def で定義された関数を呼び出すことができます}

Makefile で使用されるコマンドは次のとおりです。

%.o : %.cpp
 g++ -g -lssl -fPIC -levent -lcrypto -luuid -lpthread -lrt -lboost\_filesystem -lboost\_system -lboost_python -lpython -I/usr/include/python2.7 -o $@ -c $<

B.so を生成するコマンドは次のとおりです。

g++ -shared -Wl、-soname、libB.so -o libB.so *.o -lpython -lboost_python

動的ライブラリをPythonスクリプトに導入する必要がある

libBをインポートする

libB.add(10,20) を印刷する

上記のコマンドに従って記述およびコンパイルすることで、私が遭遇した落とし穴を回避できます。 -lpython の位置に注意してください。前に置かないでください。 オーバーロードされた定義が実装されておらず、def("LOGIN",LOGIN); が直接使用されている場合、次のエラーが報告されます: error: no matching function for call to 'def(const char [15], <unresolved overloaded function type>)' def("LOGIN",LOGIN); 要約すると、これは私が丸一日かけて調べた結果です。間違いや抜けがあれば、ご指摘ください。よろしくお願いします。

補足: boost.python を使用して C++ 動的ライブラリを呼び出す場合、参照型を処理できません。たとえば、string& recv_answer は戻り結果を受け取るために使用され、string{lvalue} として認識されますが、私の Python は文字列型を渡し、一致できません。そこで、string&recv_answerの文字列型参照を手動でchar *recv_answer_c形式に変更し、つまりC言語スタイルに変更してから、次の方法でrecv_answer_cパラメータを渡して結果を受け取りました。

#セグメンテーションエラーが発生しないように、変数にスペースを事前に割り当てるためにバイトを使用します temp = bytearray(1000)
recv_answer_c = バイト(一時)

要約:

上記はこの記事の全内容です。この記事の内容が皆さんの勉強や仕事に一定の参考学習価値を持つことを願っています。ご質問があれば、メッセージを残してコミュニケーションしてください。123WORDPRESS.COM を応援していただきありがとうございます。

以下もご興味があるかもしれません:
  • C++ で Boost.Chrono 時間ライブラリを使用する方法
  • Boost ライブラリのカスタマイズと C++ での応用の詳細な説明
  • デザインパターンのシングルトンパターンを使用して、C++ ブーストライブラリを実装します。
  • C++ boostライブラリの詳細なインストール手順

<<:  MySQL でローカル ユーザーを作成し、データベース権限を付与する方法の例

>>:  Vue3.0プロジェクトの構築と利用プロセス

推薦する

フォームを送信した後、別のファイルに移動する

<br />質問:特定のファイルにジャンプするには、HTML でどのように記述すればよい...

MySQL における「:=」と「=」の違いの簡単な分析

=設定および更新の場合にのみ、:= と同じ効果、つまり代入効果があり、それ以外の場合は等号の効果があ...

Linux マルチスレッドにおけるフォークとミューテックス ロック プロセスの例

目次質問: 1. 最初の試み2. 合理的な分析3. 問題解決(1) pthread_join()の使...

ウェブデザインにおける円形要素の使用例 25 選

本日の投稿では、Web デザインで使用される円形要素の優れた例をいくつか挙げ、美しい丸いボタン、メニ...

Docker nginx + https サブドメイン設定の詳細なチュートリアル

今日はたまたま友人のサーバーの移転を手伝うことになり、サーバーの基本的な設備の設定を行ったのですが、...

Echarts は 1 つのグラフ内で異なる X 軸を切り替える機能を実装します (サンプル コード)

レンダリング下の画像のような効果を実現したい場合は、読み続けてアニメーション画像に直接進んでください...

MySQL の簡単な分析 - MVCC

バージョンチェーンInnoDB エンジン テーブルでは、クラスター化インデックス レコードに 2 つ...

HTML でフロートをクリアする 2 つの方法

1. クリアフローティング法1前の親要素の高さを設定します。注: エンタープライズ開発では、可能であ...

mysql 8.0.18.zip のインストールと構成方法のグラフィック チュートリアル (Windows 64 ビット)

以前にインストールされたバージョンのデータベースをアンインストールする方法については、この記事を参照...

MYSQL SERVER のログファイルを縮小する方法

トランザクション ログには、関連するデータベースに対する操作が記録され、データベースの回復に関連する...

Linux viコマンドの知識ポイントと使い方のまとめ

Linux viコマンドの詳しい説明vi エディタは、すべての Unix および Linux システ...

React は antd のアップロード コンポーネントを使用してファイル フォーム送信機能を実装します (完全なコード)

私はプロジェクトを実行するために react を使い始めたばかりで、非常に未熟で完全な初心者です。私...

CSS の複数行テキストがオーバーフローする場合の省略記号の例

複数行のテキストがオーバーフローすると省略記号が表示されますこの記事では 2 つの方法を推奨します。...

HTML でフォームを中央揃えにする

以前、写真が与えられ、その写真スタイルに基づいてフォームを作成するという課題に遭遇しました。しかし、...

MySQL 5.5.27 インストール グラフィック チュートリアル

1. MYSQLのインストール1. ダウンロードしたMySQLインストールファイルmysql-5.5...