Портируем код C/C++ на Python

В первой части нашего небольшого цикла статей мы исследовали возможность использования структур данных в Python, которые были созданы с помощью языков программирования C/C++. Для этого мы рассмотрели библиотеку Ctypes.

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

Библиотека CFFI

Библиотека для работы с С из языка программирования Python. Написана с использованием pycparser, то есть это полноценный парсер языка программирования C и еще несколько дополнительных функций. Как это работает? Библиотека позволяет определять прототипы функций и производить их компиляцию. После проведения этих операций из Python можно вызывать функцию C, как будто она была определена изначально на Python.

Чтобы можно было использовать библиотеку, достаточно ввести вот такую команду:

pip install cffi

Для примера того, как можно работать с этой библиотекой, проведем уже известный из предыдущей статьи эксперимент. Попробуем вызвать MessageBoxA из системной библиотеки. Вот так будет выглядеть код:

from cffi import FFI def main(): ffi=FFI() ffi.cdef(""" int MessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCation, UINT uType); """) _user32 = ffi.dlopen("USER32.DLL") lpText = bytes("Hello from cffi", "utf-8") lpCaption = bytes("Test cffi", "utf-8") MB_OK = 1 if _user32.MessageBoxA(ffi.NULL, lpText, lpCation, MB_OK): print("MessageBox showed!") if __name__ == "__main__": main()

Результат выполнения скрипта будет таким:

Код стал проще и лаконичнее, теперь можно просто определять функции, копируя их из документации. Все типы, перечисления больше не нужно определять самостоятельно: всё это добавлено уже самой cffi. Но если очень хочется, то можно сделать это вручную. В коде ниже представлен пример того, как можно с помощью библиотеки cffi сделать структуру и присвоить значения её полям:

from cffi import FFI def main(): ffi = FFI() ffi.cdef(""" typedef struct { unsigned char one, two; } test; """) testStruct = ffi.new("test[]", 1) testStruct[0].one = 255 testStruct[0].two = 255 if __name__ == "__main__": main()

Все работает так же, как если бы мы просто создавали кусочки быстрого и эффективного кода на С. А что же насчет С++? К сожалению, эта библиотека не умеет работать с С++, но есть достойный аналог — cppyy.

cppyy

Библиотека для автоматической компиляции и работы с С++ из Python и наоборот. Компиляция и работа с кодом осуществляется в рантайме, поэтому конструкции языка С++ можно определять так же, как это было для С в cffi.
Cppyy построена поверх интерпретатора cling. Cling умеет работать с C++ благодаря тому, что использует clang и LLVM. По факту, это является способом быстрой разработки и прототипирования для С++.

Библиотека устанавливается достаточно просто:

pip install cppyy

Так как для работы библиотеке нужен так называемый backend, то при установке будут собираться расширения для её работы. Поэтому стоит установить Build Tools для сборки С++ приложений от MS. После установки всего необходимого нужно разобраться с принципом работы библиотеки.

Разработчики уверяют, что библиотеку можно использовать для всех конструкций языка. Однако стоит иметь в виду некоторые особенности. Библиотека для своей работы определяет объекты, которые в Python будут использоваться для корректной работы языка С++:

  1. cppyy определяет один общий namespace — cppyy.gbl. Именно здесь можно будет найти объекты, которые мы будем создавать для тестирования функций языка С++.

  2. Если требуется создать новый namespace, он будет присоединяться к глобальному — cppyy.gbl.newSpace.

  3. Для адаптации конструкций под синтаксис Python можно описывать переменные из класса с помощью lambda выражений.

  4. Кусочки или целый алгоритм можно определять через функцию cppyy.cppdef.

Рассмотрим простой пример класса с конструктором. Этот класс будет на этапе создания объекта присваивать значения для переменной внутри класса. Для знакомства с библиотекой лучше использовать интерактивный шелл от Python, подойдет и IDLE. Ниже кусочки кода, которые можно вводить и изучать работу cppyy. Такой подход используется не случайно, потому чтобы работать с любым объектом из cppyy, нужно каждый раз запускать процедуру отправки данных в cling, что требует времени. Поэтому мы сначала определяем класс или namespace и потом их нужно импортировать в Python для использования.

import cppyy # Определим простой класс
cppyy.cppdef(""" class Test { public: Test(int i) : m_data(i) {} int m_data; };""")
# Чтобы им воспользоваться, нужно его импортировать из глобального namespace from cppyy.gbl import Test # Теперь можно использовать класс в коде test = Test(19) # test - объект, который можно использовать через Python
# Попробуем использовать параметр из объекта print(test.m_data)

Можно также работать со стандартными типами и библиотеками, например, создать вектор и инициализировать его уже в Python:

from cppyy.gbl import vector v = vector[int](range(20)) 

Все обращения к созданному вектору будут контролироваться через Python, но, если необходимо, также работать с низкоуровневым представлением памяти и объектов, то есть отдельный набор функций cppyy.ll. Ниже пример, как можно выделить кусок сырых данных через malloc.


import cppyy.ll array = cppyy.ll.malloc[int](10)
array[0] = 1
array[1] = 2 cppyy.free(array) 

И крайний пример, как можно определить виртуальную функцию класса на С++:

import cppyy cppyy.cppdef(""" class MyTest { public: MyTest(int i) : m_data(i) {} virtual ~MyTest() {} virtual int add_int(int i) { return m_data + i; } int m_data;
};""") from cppyy.gbl import MyTest m = MyTest(10) cppyy.cppdef(""" void say_hello(MyClass* m) { std::cout << "Hello, the number is: " << m->m_data << std::endl; }""") MyTest.say_hello = cppyy.gbl.say_hello m.say_hello() 

Вот такие интересные проекты существуют для стирания границ между языками программирования. Благодаря чему это возможно? Библиотеки, расмотренные в этой статье, по сути просто обертки вокруг функций LLVM и clang. О чем эти технологии и как ими можно пользоваться из Python, разберемся в следующей статье.

Читать еще:


Также приглашаю всех желающих на бесплатный демоурок в рамках которого обсудим различные виды типизации, заглянем в теорию типов, рассмотрим примеры и best practice по аннотированию в Python, а также поговорим про существующие type checker’ы. Регистрация уже доступна по ссылке.

Читайте так же:

  • Минцифры и «Ростелеком» интегрируют государственную биометрическую систему с сайтом «Госуслуги»Минцифры и «Ростелеком» интегрируют государственную биометрическую систему с сайтом «Госуслуги» По информации издания «Коммерсантъ», Минцифры заключило с «Ростелекомом» контракт на интеграцию единой системы идентификации и аутентификации (ЕСИА) сайта «Госуслуги» с государственной информационной системой «Единая биометрическая система» (ГИС ЕБС), Госкомпания выполнит это за 1,2 млрд […]
  • Тестирование MySQL на ARM-архитектуреТестирование MySQL на ARM-архитектуре Привет, Хабр!Мы в ECOMMPAY, помимо прочего, очень любим MySQL и «железные» серверы. MySQL используется как основная СУБД для нашего прода, и, кажется, мы умеем готовить её хорошо для высоких нагрузок. Так же хорошо (а может, и лучше) мы умеем работать с baremetal: они понятно […]
  • Dell представила концепцию ремонтопригодного и экологичного ноутбукаDell представила концепцию ремонтопригодного и экологичного ноутбука Dell анонсировала новую концепцию дизайна ноутбука, который долговечен, легко разбирается и ремонтируется и при этом более экологичен. Ноутбук Luna имеет ряд необычных функций, которые призваны облегчить его ремонт и обслуживание. The VergeТак, для его разбора не требуются отвертки […]
  • Интересные проекты на Raspberry Pi: от счетчика Гейгера до коммерческих серверовИнтересные проекты на Raspberry Pi: от счетчика Гейгера до коммерческих серверов Raspberry Pi, наверное, самые популярные одноплатники в мире. Большинство моделей недорогие, а их возможности позволяют использовать платы в очень широком спектре самых разных проектов. Последние могут быть как очень простыми — например, управление светодиодной подсветкой, до чрезвычайно […]