【问题标题】:Python ctypes function returns ValueError when C function returns NULL当 C 函数返回 NULL 时,Python ctypes 函数返回 ValueError
【发布时间】:2020-12-15 23:34:18
【问题描述】:

我正在使用 ctypes 在 Python 中创建一个到 C 库的接口,并且我有一个 C 函数通常返回一个 char**(C 字符串数组),但在出错时返回 NULL。我不知道返回的数组的长度,最后一个条目将为 NULL。我已经为 restype 尝试了多种类型:

restype = POINTER(c_char_p)
restype = POINTER(POINTER(c_char))

当 C 函数成功返回时,这两种方法都可以正常工作(之后的处理略有不同)。但是当 C 函数有错误并返回 NULL 时,两者都有问题。我原以为返回值为 None,而是获取指向堆栈跟踪的字节字符串的指针,该指针以“ValueError:NULL 指针访问”结束。

一个 C 函数的 restype 应该是什么,它在成功时将 char** 转换为字符串数组,但在错误时将 NULL 转换为 None?

【问题讨论】:

    标签: python ctypes


    【解决方案1】:

    几个选项:

    • 使用c_void_p 结果。如果返回NULL,它将被强制转换为None,否则将其转换为POINTER(c_char_p)以提取字符串。
    • 使用POINTER(c_char_p) 提取字符串。包裹在 try/except 中并在 ValueError 上返回 None

    两者都有效。我更喜欢使用包装器来使函数按预期运行,并确保如果字符串是动态分配的,一旦提取为 Python 字符串,它们就会被释放。

    例子:

    test.c

    #if defined(_WIN32)
    #   define API __declspec(dllexport)
    #else
    #   define API
    #endif
    
    #include <stdlib.h>
    #include <string.h>
    
    API char** get_strings(int fail) {
        if(fail)
            return NULL;
        char** p = malloc(sizeof(char*) * 3);
        p[0] = _strdup("test1");
        p[1] = _strdup("test2");
        p[2] = NULL;
        return p;
    }
    
    API void free_strings(char** p) {
        if(p) {
            char** tmp = p;
            while(*p)
                free(*p++);
            free(tmp);
        }
    }
    

    test.py

    from ctypes import *
    
    dll = CDLL('./test')
    dll.get_strings.argtypes = c_int,
    dll.get_strings.restype = POINTER(c_char_p)
    dll.free_strings.argtypes = POINTER(c_char_p),
    dll.free_strings.restype = None
    
    def get_strings(fail):
        p = dll.get_strings(fail)
        result = []
        try:
            for s in p:
                if s is None: break
                result.append(s)
            return result
        except ValueError:
            return None
        finally:
            dll.free_strings(p)
    
    print(get_strings(0))
    print(get_strings(1))
    

    输出:

    [b'test1', b'test2']
    None
    

    【讨论】:

    • 我将 restype 切换为 c_void_p,然后转换为 POINTER(POINTER(c_char)),因为这与我之前所做的基本一致。这奏效了。我几乎想为此提交一个错误报告,我认为 NULL 应该在我拥有的两种原始返回类型上都被强制为 None 。我确实检查了 None,它没有工作,因为那里有一些价值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-31
    • 2017-07-19
    • 1970-01-01
    • 2013-05-15
    相关资源
    最近更新 更多