Использование модуля ctypes для вызова функций C / C ++ в Python
Прочитав эту статью, вы научитесь создавать и реализовывать класс-оболочку для вызова функций C / C ++ непосредственно в Python. Мы будем использовать встроенный модуль ctypes
. Согласно официальной документации ctypes - это
«Внешняя библиотека функций для Python. Он предоставляет типы данных, совместимые с C, и позволяет вызывать функции в библиотеках DLL или совместно используемых библиотеках. Его можно использовать для обертывания этих библиотек на чистом Python ».
В этом руководстве есть три раздела:
- Настраивать
- Реализация
- Заключение
Перейдем к следующему разделу, чтобы настроить необходимые материалы для этого урока.
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.
Спасибо, что прочитали эту статью. Надеюсь увидеть вас снова в следующей статье!