[Python] 使用 ctypes 要小心指定函式的參數型別

[Python] 使用 ctypes 要小心指定函式的參數型別

專案程式有用 python 的 ctypes 呼叫一個用 C 寫的 shared library,

今天遇到一個奇怪的問題,用 python 從 PostgreSQL 讀進來的一個 BIGSERIAL 值,

傳到 shared library 之後值就錯掉了…

PostgreSQL 的 BIGSERIAL 理論上要可以支援 1~9223372036854775807,

但是我們傳進一個小於 9223372036854775807 的值,值卻會亂掉…

 

舉例來說,下面是一個用 C 寫的 shared library,

foo() 這個函式可以接受三個參數,注意最後一個是 long long 的型態:

#include <stdio.h>
extern "C"
{
void foo(unsigned int a, const char* b, long long c);
};
void foo(unsigned int a, const char* b, long long c)
{
printf("a=%d, b=%s, c=%lld\n", a, b, c);
}

 

而在 python 這邊,用 ctypes 載入 shared library 之後,用 argtypes 指定函式的參數型態,

但因為程式修改時有些錯誤,因此最後對應到參數 c 的型態沒有被修正成 ctypes.c_longlong:

import ctypes
dll = ctypes.CDLL("libtest.so")
# Get import function of SetDebugFunction
dll.foo.argtypes = [ctypes.c_uint, ctypes.c_char_p, ctypes.c_int]
dll.foo.restype = None
dll.foo(123, "456", 789012345678)

 

因為這個失誤,導致函式雖然可以呼叫,但值是不正確的:

testuser@localhost ~ $ python testlib.py
a=123, b=456, c=-1261636786

 

修正方法就是要確保 python 這個定義的函式參數型別,和 shared library 的函式參數型別都是一致的:

dll.foo.argtypes = [ctypes.c_uint, ctypes.c_char_p, ctypes.c_longlong]

 

修正之後,程式執行就沒有問題囉:

testuser@localhost ~ $ python testlib.py
a=123, b=456, c=789012345678

 

參考資料:python ctypes

(本頁面已被瀏覽過 2,013 次)

2 thoughts on “[Python] 使用 ctypes 要小心指定函式的參數型別

    1. 就是指定這個函式的回傳值型態。
      像是例子中的這個 C 函式:

      void foo(unsigned int a, const char* b, long long c);

      它的回傳值是 void,對應到 python 就是 None
      dll.foo.restype = None

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

這個網站採用 Akismet 服務減少垃圾留言。進一步了解 Akismet 如何處理網站訪客的留言資料