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プロジェクトの構築と利用プロセス

推薦する

MySQL のスケジュールされた完全なデータベースバックアップ

目次1. MySQLデータのバックアップ1.1、データをバックアップするためのmysqldumpコマ...

MySQLバックアップとリカバリの実践に関する詳細な説明

1. mysqlbackup の紹介mysqlbackup は、MySQL Enterprise B...

vue で wangEditor を使用する方法と、データをエコーし​​てフォーカスを取得する方法

バックグラウンド管理プロジェクトを行う際には、リッチテキストエディタがよく使用されます。ここでは、非...

Nginx レイヤー 4 負荷分散構成ガイド

1. レイヤー4負荷分散の概要レイヤー 4 ロード バランシングとは何ですか?いわゆる 4 層負荷分...

HTMLファイルで外部CSSファイルを導入する場合のパスの書き方について簡単にまとめます

1. 外部CSSファイルの基本スタイルをインポートする<link> タグを使用して外部ス...

MySQL プロジェクトでトランザクション分離レベルを選択する方法

導入コンテンツから始めましょう。誰もが次のような面接のシナリオに遭遇したことがあると思います。インタ...

Vueでスケルトンスクリーンを実装する例

目次スケルトンスクリーンの使用Vueアーキテクチャスケルトンスクリーンアイデアの概要抽象コンポーネン...

HTML チュートリアル: HTML 水平線分

<br />このタグを使用すると、画面上に水平線を表示して、ページのさまざまな部分を区切...

MYSQL マスタースレーブ レプリケーションの知識ポイントの概要

単一の MYSQL サーバーが現在の Web サイトのトラフィックに対応できない場合の最適化ソリュー...

Vueの監視方法のケースの詳細な説明

Vueでの監視方法時計知らせ名前: 監視する属性に同じ名前を付ける必要があります。 1. 機能Vue...

Linux学習におけるmkdirコマンドの詳しい説明

目次序文1. ファイルの概念に関する基礎知識2. mkdir コマンド序文最近、Linux にますま...

VMware Workstation 12 Pro Linux インストール チュートリアル

この記事は、VMware Workstation 12 ProのインストールLinuxチュートリアル...

Gojs がアリのラインアニメーション効果を実装

目次1. Gojsの実装1. 描画2. 破線の実装3. 点線を動かす2. 点線と点線アニメーションの...

Linux スワップメモリ​​を拡張する方法

スワップ メモリとは、主に物理メモリが不足している場合に、システムがハード ディスク領域の一部をサー...

Linux でファイルの権限 (所有権) を変更する

Linux と Unix はマルチユーザー オペレーティング システムであるため、ファイルの権限と所...