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

GuDron

dumpz.ws
Admin
Регистрация
28 Янв 2020
Сообщения
7,552
Реакции
1,435
Credits
24,378
Портируем код C/C++ на Python
фронт.jpg
В этой статье попробуем разобраться, какие еще есть подходы для работы с языками С/С++ и их аналогами, и также рассмотрим возможность анализа С/С++ исходников языков программирования и их компиляции налету. К тому же ответим на вопрос, а можно ли сделать интеграцию C/C++ в языке программирования Python прямо в скрипте. Подобный подход очень размоет границы между языками программирования, но это очень интересный функционал, который возможно может быть полезным.

Библиотека CFFI​

Библиотека для работы с С из языка программирования Python. Написана с использованием Для просмотра ссылки Войди или Зарегистрируйся, то есть это полноценный парсер языка программирования 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()

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

793f93427e109b4548b67e0034ee26c0.png

Код стал проще и лаконичнее, теперь можно просто определять функции, копируя их из документации. Все типы, перечисления больше не нужно определять самостоятельно: всё это добавлено уже самой 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()

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

GuDron

dumpz.ws
Admin
Регистрация
28 Янв 2020
Сообщения
7,552
Реакции
1,435
Credits
24,378

cppyy​

Библиотека для автоматической компиляции и работы с С++ из Python и наоборот. Компиляция и работа с кодом осуществляется в рантайме, поэтому конструкции языка С++ можно определять так же, как это было для С в cffi.

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

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

Код:
pip install cppyy

Так как для работы библиотеке нужен так называемый backend, то при установке будут собираться расширения для её работы. Поэтому стоит установить Для просмотра ссылки Войди или Зарегистрируйся для сборки С++ приложений от 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.