在 python 中透過 ctypes 執行 C++ library 的 class

動機

目前大部份的程式都是用 C++ 來實作,不過有時候會有使用 python 比較方便的時候(例如做 API 的整合測試),這時候可能會在 python 實作一些和 C++ unit test 中一樣的邏輯,但直接重寫一次很容易在日後修改時發生行為不一致的狀況,因此讓 python 能夠直接使用 C++ 的 library 就是比較好的選擇

解法選擇

  1. Boost.Python
  2. Python ctypes library
除非本來的 project 就有使用到 boost,否則要將 Boost.Python compile 起來是需要花一些工夫的,這邊會比較建議使用 ctypes library,但是 ctypes 只能夠支援 C 的 interface,這篇文章會教你如何將 C++ 的 class 透過 wrapper 的方法轉到 python 上,這方法是幾個月前從 stackoverflow 上看到的,我覺得比較容易讓 python 和 C++ 上對該 class 的使用體驗一致,分享給大家

Example

完整的範例程式碼都有放在 github 上

程式解說


每個檔案的說明如下:
  1. library.h library.cpp: 實作 class
  2. main.cpp: 使用 library.cpp 提供的 class 做 demo
  3. library-python.cpp: 提供介面給 ctypes
  4. library.py: 透過 ctype 提供 python 版本的 class
  5. ctypes-demo.py: 如同 main.cpp,使用 library.py 提供的 class
由於 cytpes 只支援 C 的介面,因此我們需要將 class 的每個操作都寫成 function 的形式(包括 constructor、destructor),並透過指標來 access member,這邊列出一部份的 code
TestClass* TestClass_new()
{
return new TestClass();
}

int TestClass_member(TestClass* ptr)
{
return ptr->member();
}
接著在 python 端,就可以透過 ctypes 再把這些 function 包成 python 的 class,我這邊是使用 python3 做測試,一樣截取其中片段
import ctypes;

lib = ctypes.CDLL('./liblibrary-python.so');

class TestClass(object):

def __init__(self):
self.obj = lib.TestClass_new()

def member(self):
return lib.TestClass_member(self.obj)
這樣一來,不論我們是在 cpp 或是 python 都可以用幾乎一樣的方式來使用該 class
  • CPP
TestClass obj;

obj.IncreaseMember();

obj.MultiplyMember(3);

std::cout << "obj.member: " << obj.member() << std::endl;
  • Python
obj = TestClass()

obj.IncreaseMember()

obj.MultiplyMember(3)

print('obj.member: ' + str(obj.member()))
不過這個方法要在 cpp 和 python 都寫一份幾乎一樣的介面,如果可以用程式來產生 library-python.cpp 和 library.py 的話會更有效率

留言

張貼留言

這個網誌中的熱門文章

unit testing 的第一步:使用 gcov/lcov 統計 c++ project 的 testing coverage

在 blogger 寫 markdown 最接近完美的辦法