序文 最近、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 関数を呼び出すと、次のエラーが報告されます。
つまり、追加機能はまだ認識されません。使用 nm B.so | grep 追加 取得できる
その結果、最初の 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 は絶対パスを追加した後もエラーを報告します。
これはバージョン番号が間違っていることを意味します。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 を応援していただきありがとうございます。 以下もご興味があるかもしれません:
|
<<: MySQL でローカル ユーザーを作成し、データベース権限を付与する方法の例
MySQL のインストールに関する以前のメモを要約して、皆さんと共有しました。ステップ 1: mys...
Dockerのインストール手順をスキップする1. postgresqlイメージを取得する docke...
みなさんこんにちは。私は梁旭です。 Linux を使用しているときに、いくつかのコマンドを連結する必...
この記事では、Baidu News Navigation Barの効果を実現するための具体的なJSコ...
目次1. プロキシとは何ですか? 2. 使い方は? 1. プロキシを使用する簡単な例2. 対象オブジ...
結果 (完全なコードは下部にあります): 実装は難しくありませんが、繰り返しコードが多くなります。実...
なぜ Nexus プライベート サーバーを構築する必要があるのでしょうか。その理由は非常に簡単です。...
達成される効果多くの場合、入力ボックスの値の変化をリアルタイムで監視し、ブラウザを誘導してウェブサイ...
目次全体的な効果コンテナのスクロールイベントをリッスンするストア内の構成ページが戻るときのスクロール...
データベース アプリケーションは、アプリケーション システムに不可欠な部分です。リレーショナル デー...
目次1. 自己列挙可能なプロパティ2. Object.values()はプロパティ値を返します3. ...
目次1. 基本的な例2. 計算プロパティキャッシュとメソッド3. 計算プロパティセッター序文:通常、...
関連記事:初心者が学ぶ HTML タグ (2)導入された HTML タグは、必ずしも XHTML 仕...
1. なぜこの記事を書くのですか?重複リクエストの処理に関する記事をたくさん読んだことがあるでしょう...
最近、docker load -i コマンドを使用してイメージ パッケージを圧縮した後、イメージ名と...