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:
Для инструмента Beautiful Capi вам необходимо создать следующий XML-файл:namespace HelloWorld { class PrinterImpl { public: void Show() const { std::cout << "Hello Beautiful World!" << std::endl; } }; }
<Э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 и задачах, которые он решает. Количество и качество обзоров побудят автора написать продолжение или расширить данную статью.
Ссылки
- Инструмент Красивая Капи
- Двоичный интерфейс приложения, АБИ
- Бесплатный инструмент для связывания программ и библиотек.
-
Прегль, Фриц
19 Oct, 24 -
Объяснение Микрофронтендов
19 Oct, 24 -
Avito Playbook: Первоначальный Коммит
19 Oct, 24 -
Опубликован Код Алгоритма Predator
19 Oct, 24 -
Сохрани Мысль...
19 Oct, 24