Ctypes. Как передать структуру по ссылке? - PullRequest
1 голос
/ 25 марта 2020

Я пытаюсь написать оболочку Python для библиотеки C, используя ctypes. Пока у меня есть:

C .h


    typedef struct
    {
        int erorrCode;
        char * Key;

    } A;

    #ifdef __cplusplus
    extern "C" {
    #endif

    EXPORT void __stdcall DestroyA(A &input);

    #ifdef __cplusplus
    }
    #endif

C. cpp


    EXPORT void __stdcall DestroyA(A &input)
    {
        delete []input.Key;
    }

Python .py


    import sys
    import ctypes

    class A(ctypes.Structure):
        _fields_ = [
            ("erorrCode", ctypes.c_int),
            ("Key", ctypes.c_char_p)]


    try:
      libapi = ctypes.cdll.LoadLibrary('./lib.so')
    except OSError:
      print("Unable to load RAPI library")
      sys.exit()

    DestroyA = libapi.DestroyA
    libapi.DestroyA.argtypes = [ctypes.POINTER(A)]
    libapi.DestroyA.restype = None

    a = A(1,b'random_string')

    DestroyA(ctypes.byref(a)) #!!!here is segmentation fault 

Итак, как я могу исправить ошибку ошибки сегментации?

Примечание: я не могу изменить код на C ++, если есть способ исправить это на стороне Python.

1 Ответ

0 голосов
/ 25 марта 2020

Листинг [Python 3.Docs]: ctypes - библиотека сторонних функций для Python.

У вас есть Неопределенное поведение ( UB ).

Python имеет встроенное управление памятью для своих объектов, включая CTypes .
Итак, каждый раз, когда объект ( PyObject , который является в основном чем-либо - включая Python int ), Python вызывает одну из функций mallo c под капотом для выделения памяти. И наоборот, когда объект уничтожен (вручную или G C), вызывается free .

Что произошло:

  1. Вы создали объект (за кадром Python выделил немного памяти)
  2. Вы вызвали free на объекте, выделенном Python (что неверно, не говоря уже о том, что вы также пересекли границу .dll )

Вам нужно бесплатно только на указатели, которые вы выделили . Один из таких примеров: [SO]: python: ctypes, прочитайте POINTER (c_char) в python (ответ @ CristiFati) .

Если вы хотите избавиться от объекта (и, таким образом, освободите используемую память), пусть Python сделает это за вас:

del a

Дополнительные замечания:

  • Вы используя __stdcall функции с ctypes.CDLL . Опять же, это UB (на 32bit ). Используйте "обычное" соглашение о вызовах ( __ cdecl )
  • Вы передаете ссылку. Это C ++ speci c (хотя это всего лишь const ptr ). Чтобы быть C совместимым, используйте:

    EXPORT void destroyA(A *pInput);
    
...