Ещё один “подход” к языку daScript от Gaijin. В последней заметке про использование байндингов к OpenGL в daScript, я упоминал про наличие скрипта dasClangBind, позволяюшего генерировать байндинги к библиотекам на C и C++. Так как из документации к скрипту только совет автора Use it, abuse it
, то неплохо попробовать его в деле, чтобы разобраться, что он умеет/не умеет.
С помощью этого генератора байндингов сделаны обёртки над: dasRequests, dasPhys2d, а также добавленные в основной репозиторий dasGLFW, dasBGFX, dasImgui, dasOpenGL, dasClangBind
(привязки для генератора байндингов тоже сгенерированы им самим).
dasClangBind
не собирается с дефолтными настройками cmake, поэтому сначала необходимо включить его сборку. В файле CMakeLists.txt видим настройку пути с libclang
:SET(PATH_TO_LIBCLANG ${PROJECT_SOURCE_DIR}/../libclang)
Можно скачать скомпилированные библиотеки LLVM (yay, даже для windows!), и указать путь к скачанной библиотеке с cmake-файле. Дальше перегенерируем решение с помощью команды generate_msvc_XXX.bat
и компилируем проект libDasModuleClangBind
.
Получаем библиотеку libDasModuleClangBind.lib
, которую можно подключить для статической линковки из проекта, который будет использовать этот модуль (командой TARGET_LINK_LIBRARIES
). В сгенерированном решении убеждаемся в том, что линкер подключает библиотеки libDasModuleClangBind.lib
и libclang.lib
:
Далее в коде подключаем заголовочный файл и макрос добавления модуля в daScript:
...
int main( int, char * [] ) {
NEED_ALL_DEFAULT_MODULES;
NEED_MODULE(Module_dasClangBind); //<---
}
Теперь в das-скрипте можно импортировать модуль cbind
, который предоставляет функции-обёртки над библиотекой clang
, а главное — cbind_boost
, классы, с помощью которых можно настроить поведение генераторы, без низкоуровневого обращения к c-апи clang-а:
libclang
Для начала лучше бегло ознакомиться с тем, что умеет libclang
:
Using libclang to Parse C++
Пример разбора C++ кода с помощью libclang на Python
Choosing the Right Interface for Your Application
DasGenBind
У генератор привязок dasClangBind
, есть 2 режима: генерация обёрток над функциями в виде daScript (с помощью ffi-интерфейса dasbind
) — DasGenBind
, и более мощная генерация “обвязочного” c++-кода библиотеки — CppGenBind
. С помощью DasGenBind
сгенерированы байндинги к OpenGL, так как сама библиотека language-agnostic, и её обвязки тривиальны — используются только функции и примитивные типы.
CppGenBind
Более интересно посмотреть на байндинги к glfw, по которым можно приблизительно понять, что генерируется автоматически, а что необходимо добавлять в исключения и дописывать руками. dasClangBind
в ходе своей работы пишет, какие объявляния функций он пропускает (код - поиск по ключевому слову skip
):
- шаблоны
- функции с аргументами-указателями на функции (к примеру, колбеки)
- чисто виртуальные функции
- глобальные операторы
Также генерируются, но вызывают последующие ошибки компиляции, функции (в лучших традициях текстов ошибок шаблонов C++), получающие аргументы POD-типов по значению.
Для таких функций предполагается добавление их в список пропускаемых при автоматической генерации и последующее написания обработчика вручную:
Для дописывания несгенерированных автоматически функций предусмотрен файл MODULENAME.main.cpp
. На выходе генератор байндингов выдаёт пачку и кусок cmake-файла в stdout, с помощью которого можно собрать их в модуль. На практике почему-то у меня не создавались файлы MODULE.func.reg.inc
и MODULE.func.decl.,inc
, их для теста заполнил руками.
CmakeList.txt
В качестве шаблона cmake-файла можно взять готовый из других модулей, основная логика:
- собрать модуль из сгенерированных файлов (это за нас выводит сам
dasClangBind
) - подключить lib-файл самой C++ библиотеки, для которой делается обвязка
- определить переменные сборки daScript, позволяющие отключить модуль по желанию пользователя
Генерация байндингов для библиотеке MessagePack
Исходя из ограничений генератора, для учебного примера проще всего выбрать для примера максимально простую библиотеку, имеющую C, а не С++-интерфейс. Например - MessagePack.
Стартовый код генератора:
Такой скрипт генерирует привязки к библиотеке, однако при её компиляции возникают несколько ошибок вида:использование неопределенного типа "das::cast<TT>" libDasModuleMsgpack \daScript\include\daScript\simulate\simulate.h
Необходимо добавить эти функции в список исключаемых из генерации://передача в качестве аргумента POD-объекта по значению
msgpack_object_print
msgpack_object_print_buffer
msgpack_object_equal
//какие-то непонятки с передачей некоторых из базовых типов из C++ в daScript?
msgpack_pack_char
msgpack_pack_long
msgpack_pack_unsigned_long
Убрав их из генерации, получаем компилирующийся модуль daScript dasMsgpack
.
Тестовый скрипт
Попытаемся портировать тестовый пример библиотеки с MessagePack
с языка C на daScript:
Некоторые функции из примеры не попали в автоматическую обвязку, поэтому необходимо дописать обвязку для них вручную в файле msgpack.main.cpp
://передаём 3-й параметр по умолчанию
void das_msgpack_packer_init(msgpack_packer* pk, void* data) {
msgpack_packer_init(pk, data, msgpack_sbuffer_write);
}
//передаём параметр по указателю, не по значению
void das_msgpack_object_print(msgpack_object* o) {
msgpack_object_print(stdout, *o);
}
//меняем тип второго указателя на const char* вместо неизвестного генератору void *
void das_msgpack_pack_str_body(msgpack_packer* pk, const char* b, size_t l) {
msgpack_pack_str_body(pk, b, l);
}
void Module_msgpack::initMain() {
//добавляем функции в модуль
addExtern<DAS_BIND_FUN(das_msgpack_packer_init)>(*this,lib,"msgpack_packer_init",
SideEffects::worstDefault,"das_msgpack_packer_init");
addExtern<DAS_BIND_FUN(das_msgpack_object_print)>(*this, lib, "msgpack_object_print",
SideEffects::worstDefault, "das_msgpack_object_print");
addExtern<DAS_BIND_FUN(das_msgpack_pack_str_body)>(*this, lib, "msgpack_pack_str_body",
SideEffects::worstDefault, "das_msgpack_pack_str_body");
}
Теперь скрипт работает и выдаёт корректный результат десериализации объекта:[1, true, 3.000000, "example"]
Дальнейшая работа над модулем может предполагать:
- возможность устанавливать daScript колбеки (пример из dasGlfw)
- daScript-обвязка над POD-структурой msgpack_object и корректная передача исключенных типов(?)
- написание “daScript-ивизирующей” обёртки msgpack_boost, для перехода от с-апи к более удобной работы с библиотекой