Использование модуля ctypes для вызова функций C / C ++ в Python

Прочитав эту статью, вы научитесь создавать и реализовывать класс-оболочку для вызова функций C / C ++ непосредственно в Python. Мы будем использовать встроенный модуль ctypes. Согласно официальной документации ctypes - это

«Внешняя библиотека функций для Python. Он предоставляет типы данных, совместимые с C, и позволяет вызывать функции в библиотеках DLL или совместно используемых библиотеках. Его можно использовать для обертывания этих библиотек на чистом Python ».

В этом руководстве есть три раздела:

  1. Настраивать
  2. Реализация
  3. Заключение

Перейдем к следующему разделу, чтобы настроить необходимые материалы для этого урока.

1. Настройка

В этом уроке я буду использовать Python 3.7.4. Не стесняйтесь использовать другие версии Python, если вы можете вызывать встроенный модуль ctypes. Настоятельно рекомендуется создать виртуальную среду для этого проекта.

Убедитесь, что у вас есть dll, готовый для использования нашим файлом Python. Для создания оболочки вам понадобится только dll файл. Заголовочный файл не является обязательным, если у вас есть доступ и вы знаете доступные функции и параметры. Если вы используете сторонние библиотеки, обязательно получите соответствующие файлы заголовков для справки при написании оболочки Python.

Пожалуйста, ознакомьтесь с моим руководством Как создавать библиотеки с динамической компоновкой C / C ++ в Windows, если вы собираетесь скомпилировать свою собственную DLL.

Создайте новый файл Python в том же каталоге, что и файл dll. Я просто назову его add_wrapper.py. Не стесняйтесь называть файл в соответствии с вашими предпочтениями.

Давайте перейдем к следующему разделу и начнем писать код Python.

2. Реализация

Импортировать

Импортируйте следующий модуль в начало файла:

from ctypes import *
from sys import platform
  • ctypes - это базовый модуль для загрузки разделяемых библиотек и вызова функций внутри него.
  • sys - Мы будем использовать этот модуль для определения операционной системы и загрузки соответствующей библиотеки.

Загрузить библиотеку

Предполагая, что у вас есть две общие библиотеки для операционных систем Windows и Linux, добавьте следующий код для загрузки соответствующей библиотеки для вашей операционной системы.

В приведенном выше коде ссылка на библиотеку хранится в переменной add_lib. Мы будем использовать его для вызова функций, доступных позже.

При запуске файла Python вы должны увидеть следующий результат:

<CDLL './add.dll', handle ... at ...>

Заголовок

Давайте посмотрим на файл заголовка для этого dll.

Он написан на C ++ и имеет три функции. Все функции принимают два целочисленных параметра и возвращают целочисленную переменную. В реальной жизни все намного сложнее, со сложными типами данных и указателями.

Типы данных

Прежде чем продолжить, давайте посмотрим на доступный тип данных, предоставляемый ctypes, который представляет тот же тип данных в программировании на C.

Вы можете инициализировать тип данных, как показано ниже:

a = c_int()

Когда вы распечатаете его, вы получите следующий результат, который указывает, что это long со значением 0.

c_long(0)

Не удивляйтесь, если на выходе получится не int. На основании официальной документации,

«На платформах, где sizeof(long) == sizeof(int), это псевдоним c_long. Так что не запутайтесь, если будет напечатано c_long, если вы ожидаете c_int - на самом деле это один и тот же тип ».

Вы можете передать параметр, который служит начальным значением.

a = c_int(25)

Чтобы получить фактическое значение, вы должны использовать атрибут value.

print(a.value)

На выходе вы должны получить 25.

Для специальных типов данных, таких как char с нулевым ограничителем (который представляет строку в Python), вы должны использовать следующий код. Такой синтаксис будет более подробно обсужден позже в подразделе Array.

serialNumber = c_char * 13

Функции

Мы будем использовать переменную add_lib, которую мы определили ранее, для вызова соответствующих функций. Помните, что у нас есть только три доступные функции на основе файла заголовка:

  • add
  • sub
  • mul
add_lib.add(2, 5)

Имя функции и типы данных входной функции должны быть точно такими же, как в заголовочном файле. Если вы попытаетесь вызвать недоступную функцию, вы увидите следующее сообщение об ошибке:

AttributeError: function 'test' not found

Использование неправильного типа данных приведет к следующей ошибке:

ctypes.ArgumentError: argument 1 : <class: TypeError>

Некоторые типы данных, например int, можно использовать напрямую, не объявляя их как ctypes типы данных, но это настоятельно рекомендуется.

a = c_int(25)
b = c_int(10)
add_lib.sub(a, b)

На выходе вы должны получить 15.

Тип возврата

По умолчанию в качестве типа данных возвращается int. Вы должны вручную определить тип возвращаемого значения с помощью атрибута restype.

result = add_lib.mul
result.restype = c_int
result(a, b)

Обратите внимание, что использование неправильного типа повлияет на значение внутри него. Вы должны использовать тот же тип данных, что и в заголовочном файле. Если вы намереваетесь получить от него другие типы данных, просто приведите его после получения результата.

Указатель

Вы можете создать новый указатель, используя следующий код:

temperature = c_int(25)
temperature_p = pointer(temperature)

Вы должны использовать атрибут contents для доступа к содержимому внутри указателя.

temperature_p.contents

Вы должны получить c_long(25) в качестве выходного значения. Вы можете комбинировать его с атрибутом value для доступа к желаемому значению.

temperature_p.contents.value

Если вам на самом деле не нужен указатель и вы просто хотите использовать его для ввода параметров, ctypes предоставляет нам функцию byref, которая используется для передачи параметров по ссылке. Следующий код предназначен для справки и не может быть запущен с add_lib dll.

temperature = c_int(25)
result = example_lib.calc_temp(byref(temperature))

Структура и союз

При работе со структурой вы должны использовать следующий код. Убедитесь, что соответствующий тип данных соответствует исходному типу данных, определенному в файле заголовка.

class lib_sdk_info(Structure):
    _fields_ = [("sdk_version_major", c_uint8),
    ("sdk_version_minor", c_uint8),
    ("sdk_build_major", c_uint8),
    ("sdk_build_minor", c_uint8)]

Вы можете создать новую структуру, используя следующий код:

info = lib_sdk_info()

Доступ к полям внутри осуществляется следующим образом:

info.sdk_build_minor

Если возникнет необходимость распечатать всю информацию внутри структуры, рекомендуется сделать это с помощью следующего for loop:

for field_name, field_type in info._fields_:
    print(field_name, getattr(info, field_name))

Множество

Лучший способ создать массив - умножить тип данных на целое число. Вы можете создать массив структур в зависимости от вашего варианта использования.

phoneArray = c_int * 10

Затем вы можете создать новый экземпляр массива и использовать его как список.

phone_list = phoneArray()
phone_list[0] = 32
for i in phone_list:
    print(i)

3. Заключение

Подведем итоги тому, что мы узнали сегодня.

Мы начали с краткого объяснения ctypes и настроили необходимый dll, который будет использоваться в нашем руководстве.

Затем мы загрузили разделяемые библиотеки в зависимости от операционной системы и подробно изучили доступные типы данных, предоставляемые модулем. После этого мы вызвали функции, доступные из dll, на основе ссылки из файла заголовка.

Мы также протестировали создание указателя и отправили его в функцию через функцию thebyref. Кроме того, мы создали соответствующие объекты Structure и Array, которые можно нормально использовать внутри нашего файла Python.

Спасибо, что прочитали эту статью. Надеюсь увидеть вас снова в следующей статье!

Ссылка