Знакомство С Beautiful Capi, Инструментом Для Создания Оболочек C++ Для Библиотек C++.

Beautiful Capi — это инструмент, который облегчает создание динамических библиотек на C++ с внешним интерфейсом на C. Этот инструмент также создает оболочки C++ для этого интерфейса C. Beautiful Capi написан на Python 3. Основная головная боль разработчиков библиотек C++ — отсутствие единого стандарта ABI. Разные компиляторы имеют разные ABI, соглашения об именах, схемы перехвата исключений и т. д. Поэтому программистам C++ приходится каждый раз брать исходники библиотеки и компилировать ее с помощью необходимого компилятора.

Это хорошо, если библиотека популярна, и добрый дядя уже выложил к ней бинарные файлы для большинства компиляторов C++.

Опять же, для большинства компиляторов.

Компиляторов C++ достаточно много, и если принять во внимание разные версии одного и того же компилятора, имеющие несовместимый ABI, то вероятность того, что уже скомпилированная библиотека вам не подойдет, довольно высока.

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

Инструмент Beautiful Capi предлагает решение этой проблемы.

Динамическая библиотека должна иметь только внешний интерфейс C. В интерфейсе следует использовать только типы, размер которых строго фиксирован, например: int32_t , uint8_t и т. д. Также не забывайте о соглашении о вызове функций.

Интерфейс C также можно использовать напрямую, если какой-нибудь программист C решит использовать вашу библиотеку.

Для программистов C++, которые могут использовать библиотеку, существуют оболочки C++, которые Beautiful Capi генерирует автоматически.

Планируется создать оболочки для других языков, таких как C# (.

NET), Java и Python. Инструмент Beautiful Capi не анализирует исходные коды C++, как это делает аналогичная система Swig. Вы должны предоставить инструменту описание общедоступного API библиотеки.

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

Планируется добавить форматы, отличные от XML, для описания публичного API. Другая серьезная проблема — менеджеры динамической памяти.

Реализация такого менеджера, используемая в библиотеке C++, может отличаться от реализации в клиентском приложении.

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

Этот инструмент гарантирует, что каждый экземпляр класса создается и удаляется с помощью диспетчера кучи библиотеки C++.

Проект Beautiful Capi имеет открытый исходный код. код и распространяется по лицензии GNU GPL. Однако это не мешает вам использовать его в собственных проектах, ведь Beautiful Capi — это, по сути, внешний инструмент, который мерзавец или какой-то другой.



Привет, мир!

Давайте рассмотрим пример первой независимой от компилятора библиотеки на C++.

В пространстве имен HelloWorld есть класс PrinterImpl. В классе есть единственный метод Show(), выполняющий нужные действия.

Собственно, сам класс PrinterImpl:

  
  
  
  
  
  
   

namespace HelloWorld { class PrinterImpl { public: void Show() const { std::cout << "Hello Beautiful World!" << std::endl; } }; }

Для инструмента Beautiful Capi вам необходимо создать следующий XML-файл:

<Эxml version="1.0" encoding="utf-8" ?> < hello_world:api xmlns:hello_world="http://gkmsoft.ru/beautifulcapi " project_name="HelloWorld"> <namespace name="HelloWorld"> <class name="Printer" implementation_class_name="HelloWorld::PrinterImpl" implementation_class_header="PrinterImpl.h" lifecycle="copy_semantic"> <constructor name="Default"/> <method name="Show" const="true"/> </class> </namespace> </ hello_world:api >

Надеюсь, здесь все достаточно ясно.

Несмотря на то, что наш класс реализации называется ПринтерИмпл , мы решили, что его публичное имя будет просто Принтер .

Атрибуты имя_класса_реализации И реализация_class_header укажите имя класса реализации и имя заголовочного файла, в котором доступно его описание.

Атрибут жизненный цикл определяет сценарий жизненного цикла объектов.

Заранее скажу, что на данный момент поддерживаются три типа семантики жизненного цикла: copy_semantic , reference_counted И raw_pointer_semantic .

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

Давайте покажем, какие функции C будут сгенерированы.

Файл, созданный с помощью инструмента Beautiful Capi. AutoGenWrap.cpp должны быть включены в библиотеку:

void* hello_world_printer_default() { return new HelloWorld::PrinterImpl(); } void hello_world_printer_show_const(void* object_pointer) { const HelloWorld::PrinterImpl* self = static_cast<HelloWorld::PrinterImpl*>(object_pointer); self->Show(); } void* hello_world_printer_copy(void* object_pointer) { return new HelloWorld::PrinterImpl(*static_cast<HelloWorld::PrinterImpl*>(object_pointer)); } void hello_world_printer_delete(void* object_pointer) { delete static_cast<HelloWorld::PrinterImpl*>(object_pointer); }

Для ясности все соглашения о вызове функций и ключевые слова extern «C» опущены.

Как мы видим, неявный аргумент этот — это первый аргумент типа void*.

Функция hello_world_printer_default не принимает аргументов, он создает объект реализации в куче и возвращает указатель на него как указатель на void. Функция hello_world_printer_show_const внутри он просто вызывает метод Show().

Функция hello_world_printer_copy копирует объект реализации и возвращает указатель на его копию.

Функция hello_world_printer_delete удаляет объект реализации.

Сгенерированная оболочка C++, файл Принтер.

h :

namespace HelloWorld { class Printer { public: Printer() { SetObject(hello_world_printer_default()); } void Show() const { hello_world_printer_show_const(GetRawPointer()); } Printer(const Printer& other) { if (other.GetRawPointer()) { SetObject(hello_world_printer_copy(other.GetRawPointer())); } else { SetObject(0); } } ~Printer() { if (GetRawPointer()) { hello_world_printer_delete(GetRawPointer()); SetObject(0); } } void* GetRawPointer() const { return mObject; } protected: void SetObject(void* object_pointer) { mObject = object_pointer; } void* mObject; }; }

Код на стороне клиента:

#include "HelloWorld.h" int main() { HelloWorld::Printer printer; printer.Show(); return EXIT_SUCCESS; }

Результат программы:

Hello Beautiful World!



Настоящая кросс-компиляция

Стоит отметить одну особенность создания динамических библиотек с помощью компилятора Microsoft Visual C++ (и не только) в операционной системе Microsoft Windows. По умолчанию для любой динамической библиотеки.

some_name.dll создается статическая библиотека какое-то_имя.

lib , который уже связан с клиентами библиотеки.

Но проблема в том, что есть два несовместимых формата.

.

lib файлы, один от Microsoft, а другой от ныне несуществующей Borland. И если мы хотим использовать нашу динамическую библиотеку в клиенте, который использует, например, компилятор MinGW GCC, нам нужно будет использовать стороннюю утилиту преобразования.

.

lib файлы или отказаться от использования .

lib файлы.

К счастью, инструмент Beautiful Capi позволяет использовать динамический загрузчик, что позволяет полностью отказаться от статических библиотек:

#include <iostream> #include <cstdlib> #define HELLOWORLD_CAPI_USE_DYNAMIC_LOADER #define HELLOWORLD_CAPI_DEFINE_FUNCTION_POINTERS #include "HelloWorld.h" int main() { try { #ifdef _WIN32 HelloWorld::Initialization module_init("hello_world.dll"); #elif __APPLE__ HelloWorld::Initialization module_init("libhello_world.dylib"); #else HelloWorld::Initialization module_init("libhello_world.so"); #endif HelloWorld::Printer printer; printer.Show(); } catch (const std::exception& exception) { std::cout << "Exception: " << exception.what() << std::endl; return EXIT_FAILURE; } return EXIT_SUCCESS; }

В случае, если динамическая библиотека hello_world.dll недоступен, исключение типа std::runtime_error с описанием ошибки.

Я успешно протестировал этот пример с помощью компилятора Cygwin Clang, двоичный файл динамической библиотеки был создан с помощью компилятора Microsoft Visual C++ 2015.

Заключение

В этой небольшой статье я не рассмотрел такие важные аспекты, как организация перехвата и обработки исключений, реализация клиентских интерфейсов, поддержка шаблонов C++ и многие другие, которые реализованы в инструменте Beautiful Capi. Также не был дан сравнительный анализ решения на базе Beautiful Capi и других инструментов, способных выполнить поставленную задачу, а именно системы генерации оберток Swig, технологии Microsoft COM и ее кроссплатформенных аналогов.

Однако данная статья дает общее представление об инструменте Beautiful Capi и задачах, которые он решает. Количество и качество обзоров побудят автора написать продолжение или расширить данную статью.



Ссылки

  1. Инструмент Красивая Капи
  2. Двоичный интерфейс приложения, АБИ
  3. Бесплатный инструмент для связывания программ и библиотек.

    глоток

Теги: #C++ #C++ #binding #wrapper #wrapper #генерация оберток C++ #генерация привязок C++ #python #C++ #компиляторы #api #C++
Вместе с данным постом часто просматривают: