はじめに
Python 標準ライブラリ ctypes を利用して、C++ メソッドに文字列を渡します。
動作環境は以下です。
- Windows 11
- WSL Ubuntu (20.04)
- g++ 9.4.0
- Python 3.8.10
サンプルコード
C++ ライブラリ
C++ 側の print
メソッドでは、文字列を受け取りこれを標準出力します。
#include <iostream>
extern "C" void print(const char *c_str)
{
std::cout << c_str << std::endl;
}
$ g++ --shared -fPIC myprint.cc -o libmyprint.so
Python スクリプト
Python 側では、Hello World!
の文字列を生成し、これを libmyprint.so
の print
メソッドに渡します。
import ctypes
import pathlib
dirname = pathlib.Path(__file__).parent.resolve()
libpath = dirname.joinpath("libmyprint.so")
lib = ctypes.cdll.LoadLibrary(libpath)
lib.print.argtypes = [ctypes.c_char_p]
lib.print("Hello World!".encode())
lib.print("こんにちは、世界!".encode())
$ python3 main.py
Hello World!
こんにちは、世界!
ポイント解説
argtypes の指定
print メソッドを実行する前に、lib.print
に argtypes
を指示してあります。
今回のケースに限れば、指定せずとも動作上問題ありませんでした。
型によっては C++ 側のメソッドに引数が正しく渡されなくなってしまいますので、argtypes
は常に明示するのが良いです。
バイト文字列への変換(encode)
lib.print
に文字列を渡す際に、.encode()
メソッドでバイト列への変換を行っています。
C++ の側のメソッドでは引数に const char *
(バイト列)を期待しているために必要な変換であります。
バイト列に変換しないと
バイト列に変換しない場合は、型不正のエラーが発生してしまいます。
import ctypes
import pathlib
dirname = pathlib.Path(__file__).parent.resolve()
libpath = dirname.joinpath("libmyprint.so")
lib = ctypes.cdll.LoadLibrary(libpath)
lib.print.argtypes = [ctypes.c_char_p]
lib.print("Hello World!")
lib.print("こんにちは、世界!")
$ python3 main.py
Traceback (most recent call last):
File "main.py", line 9, in <module>
lib.print("Hello World!")
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type
さらに argtypes も指定しないと
さらに argtypes
の指定をサボっていると、正常終了はするものの文字列が正しく渡されない結果が出力されます。
import ctypes
import pathlib
dirname = pathlib.Path(__file__).parent.resolve()
libpath = dirname.joinpath("libmyprint.so")
lib = ctypes.cdll.LoadLibrary(libpath)
# lib.print.argtypes = [ctypes.c_char_p]
lib.print("Hello World!")
lib.print("こんにちは、世界!")
$ python3 main.py
H
S0
バイト列を直接記述
なお、ASCII 文字列のみであれば、b"..."
の形式でバイト列を直接定義することも可能です。
import ctypes
import pathlib
dirname = pathlib.Path(__file__).parent.resolve()
libpath = dirname.joinpath("libmyprint.so")
lib = ctypes.cdll.LoadLibrary(libpath)
lib.print.argtypes = [ctypes.c_char_p]
lib.print(b"Hello World!")
$ python3 main.py
Hello World!
wchar_t を利用する場合
C++ の文字列は、wchar_t
によってワイド型で表現することも可能です。
サンプルとして、引数の型を const char *
から const wchar_t *
に変更します。cout
も wcout
に変更します。
#include <iostream>
extern "C" void print(const wchar_t *c_str)
{
std::wcout << c_str << std::endl;
}
$ g++ --shared -fPIC myprint.cc -o libmyprint.so
Python 側では、argtypes
に c_wchar_p
を指定するように変更します。
また、文字列はバイト列にエンコードせずそのまま渡すようにします。
import ctypes
import pathlib
dirname = pathlib.Path(__file__).parent.resolve()
libpath = dirname.joinpath("libmyprint.so")
lib = ctypes.cdll.LoadLibrary(libpath)
lib.print.argtypes = [ctypes.c_wchar_p]
lib.print("Hello World!")
lib.print("こんにちは、世界!")
$ python3 main.py
Hello World!
こんにちは、世界!
無事実行できました。