Функция Python ctypes возвращает ValueError, когда функция C возвращает NULL

Я создаю интерфейс для библиотеки C в Python, используя ctypes, и у меня есть функция C, которая обычно возвращает char ** (массив C-строк), но возвращает NULL при ошибке. Я не знаю длину возвращаемого массива, последняя запись будет NULL. Я пробовал несколько типов для restype:

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

Оба они работают нормально, когда функция C успешно возвращается (с немного другой обработкой впоследствии). Но у обоих есть проблемы, когда функция C имеет ошибку и возвращает NULL. Я ожидал, что возвращаемое значение будет None, но вместо этого получил указатель на байтовую строку трассировки стека, которая заканчивается «ValueError: доступ к указателю NULL».

Каким должен быть retype для функции C, которая преобразует char** в массив строк в случае успеха, но преобразует NULL в None в случае ошибки?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
0
514
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Пара вариантов:

  • Используйте результат c_void_p. Он будет принудительно заменен на None, если будет возвращен NULL, в противном случае он будет приведен к POINTER(c_char_p) для извлечения строк.
  • Используйте POINTER(c_char_p) для извлечения строк. Заверните в try/except и верните None на ValueError.

Либо работает. Я предпочитаю использовать оболочку, чтобы заставить функцию вести себя так, как нужно, и она гарантирует, что, если строки были динамически выделены, они освобождаются после извлечения как строки Python.

Пример:

тест.с

#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, это не сработало, потому что там было какое-то значение.

wile_e8 16.12.2020 15:37

Другие вопросы по теме