встраиваемый скриптовый язык что это
Встраиваемые языки: почему Lua?
Этот материал продолжает серию публикаций, основанных на докладах, которые мы сделали на конференции Games Gathering 2017 в декабре прошлого года. В одном из докладов была затронута тема выбора встраиваемого скриптового языка.
Что такое и зачем нужны скриптовые языки
Как уже упоминалось в предыдущем посте нашего блога, в нашей компании написан собственный движок. Сегодня речь пойдёт о том, чем мы руководствовались во время выбора скриптового языка для этого движка.
Таким образом, есть потребность в средстве для описания несложной, но всё-таки логики, без привлечения тяжёлой артиллерии программистов. Сделаем вывод — что такое для нас скриптовый язык? Это средство, которое позволит сделать разработку игр быстрее и дешевле.
Сразу возникает вопрос, а почему бы нам просто не использовать что-то вроде XML? Дело в том, что для наших целей нам часто нужны управляющие конструкции — ветвление и циклы, в то время как XML это декларативное описание.
Ещё одно преимущество скриптовых языков в том, что скрипты в проекте могут быть как кодом, так и ресурсом. И, соответственно, обновлять скриптовую часть игры можно не только вместе с кодом, то есть в ходе обычных обновлений через механизмы магазинов приложений. Но и вместе с ресурсами — то есть вместе с графическими и прочими материалами, с использованием CDN.
Требования к идеальному скриптовому языку
Сформулируем требования к идеальному скриптовому языку.
Python
Python — динамический язык, который пользуется немалой популярностью. Он характеризуется достаточно пологой кривой обучения, его довольно просто выучить. Однако изучить его как следует уже не так-то просто. Как результат, хорошие Python-программисты встречаются редко и дорого стоят. Это противоречит нашему желанию ускорить и удешевить разработку игровой логики.
Python обладает широкими возможностями, отличается хорошей производительностью. Его проблемой является неконсистентная система библиотек. Ещё одна его проблема, которая играет для нас важную роль, заключается в том, что он, на самом деле, не является встраиваемым языком. Это язык, из которого удобно вызывать библиотеки, написанные на C или C++.
По поводу возможностей по встраиванию Python можно сказать, что, например, существует Maya, где используется именно Python. Но тот, кто видел изнутри плагины для Maya, написанные на Python, согласится с нами в том, что выглядят они не очень хорошо.
В итоге можно сказать, что Python, при всех его сильных сторонах, нам не подходит. Теперь рассмотрим JavaScript.
JavaScript
JavaScript — это, без преувеличений, великий язык, который буквально захватил мир.
JavaScript — это популярный динамический язык, отличающийся пологой кривой обучения, обладающий широкими возможностями, хорошей производительностью и обширным набором библиотек.
Если нам, для построения игрового движка, нужен какой-нибудь интерпретатор языка — мы можем найти множество таких интерпретаторов. В реальности же придётся выбирать из двух подобных проектов — V8 и WebKit. И тот и другой имеют достаточно большие размеры. Как результат, если речь идёт о настольных играх, можно было бы рискнуть и включить в игру весь интерпретатор, но в случае мобильных игр нас такой вариант не устраивает.
В компании SocialQuantum есть собственный интерпретатор JavaScript, который проходит 98% тестов, мы планируем перевести этот проект в разряд опенсорсных.
В результате оказывается, что JavaScript выглядит сильным кандидатом на роль встраиваемого языка, но нам он тоже не подходит.
Тут надо отметить, что когда заходит разговор о JavaScript, следующим обычно вспоминают Haxe. Но, на самом деле, о возможности использования этого языка в качестве встраиваемого говорить нет смысла, так как Haxe, по сути, является не столько языком, сколько транс-компилятором в другие языки. А это не то, что нам нужно.
Может быть, нас устроит ActionScript или какой-нибудь другой скриптовый язык?
ActionScript
Если формально проанализировать ActionScript на соответствие вышеозначенным требованиям, то может показаться, что идеальный скриптовый язык найден. На его стороне динамическая природа, популярность, лёгкость изучения, хорошие возможности, производительность, наличие библиотек, лёгкость встраивания. Этот язык любят и помнят в игровой индустрии, на нём написано огромное количество замечательных Flash-игр. Главная проблема ActionScript заключается в том, что язык этот почти мёртв. Поэтому нас он тоже не устраивает.
AngelScript, Squirrel и другие
Помимо ActionScript существует множество скриптовых языков, таких, как AngelScript, Squirrel и другие. Среди них можно найти такие, которые, формально, почти полностью удовлетворяют нашим требованиям, но обычно это — языки, которые привязаны к их разработчику, в них бывают какие-то застарелые проблемы, которые годами не исправляются. Они, скорее всего, не слишком популярны, недостаточно хорошо документированы, по ним мало учебных материалов, у них не очень большие сообщества. Одним из следствий такого положения дел является тот факт, что их сложно изучать — хотя бы потому, что не до конца ясно — что они собой представляют и как работают.
Как видно, идеального встраиваемого языка мы пока не нашли. Что если создать собственный язык?
Создание собственного языка
Вполне возможно, что язык, разработанный внутри компании, будет идеально соответствовать её нуждам и его будет легко изучать. Но, скорее всего, такой язык не станет популярным. У такого языка либо будет минимальное количество библиотек, либо их не будет вовсе. Кроме того, сложно поверить, что в современных условиях можно создать нечто такое, что будет работать лучше, что будет обладать большей производительностью и будет проще встраиваться чем что-то, что уже есть на рынке.
Есть компании, которые разрабатывают и используют собственные языки, среди них есть и успешные игроки игрового рынка, но, скорее всего, это — не очень хорошая идея.
Рассмотрев существующие языки программирования, претендующие на роль встраиваемых и обсудив идею разработки собственного языка, перейдём к Lua.
Lua — встраиваемый язык, который выбрали мы
Lua — динамический язык. Он довольно-таки популярен, вокруг него сложилось большое сообщество, особенно — в сфере разработки игр. Он отличается весьма пологой кривой обучения. Например, в нашей компании сценарии для автотестов пишут на Lua. Стандартный вводный курс для автотестеров занимает примерно два часа, после чего человек в состоянии использовать этот язык. При этом Lua — мультипарадигменный язык. Он поддерживает функциональный стиль программирования и ООП. В результате он подходит не только для решения каких-то простейших задач, но и для более серьёзных дел, которыми занимаются профессиональные программисты.
Lua обладает хорошей производительностью и у него довольно много библиотек. Не так много, как у JavaScript, но, тем не менее, на сайте LuaForge можно найти практически всё, что может понадобиться. И, наконец, Lua очень просто встраивается, более того — он создан для того, чтобы его использовали как встраиваемый язык.
Например, вот как выглядит наша рабочая среда на основе IDE CLion от JetBrains. Здесь можно видеть созданный нами механизм автодополнения для Lua, который планируется сделать опенсорсным. Опенсорсным мы собираемся сделать и отладчик.
Мы выбрали Lua, но, когда речь заходит об использовании его в качестве встраиваемого скриптового языка, обычно приходится сталкиваться с примерно одними и теми же возражениями, которые мы сейчас рассмотрим.
Возражения по поводу использования Lua
Lua предназначен для C а не для С++
Никто не спорит с тем, что Lua — отличный встраиваемый язык. Главное, что считают его минусом, заключается в том, что он создан для использования с языком C, а не C++. Из-за этого, пытаясь применить в Lua что-то такое, что есть в C++ и нет в C, мы сталкиваемся с проблемами. Однако тут надо понимать, что проблемы эти решало множество довольно умных людей. Среди средств, решающих проблемы встраивания Lua в C++-проекты, можно отметить такие, как Luabind, Luabridge, toLua++, SQLuaHost. Это — далеко не полный список. Они обладают разными достоинствами и недостатками, но, тем не менее, скорее всего, всё, что вам может потребоваться, уже реализовано в одном из этих решений.
Рассмотрим, например SQLuaHost. Это — биндинг, который сделан внутри компании SocialQuantum, и который планируется сделать опенсорсным. Это решение представляет наше видение того, как должен биндиться Lua. Поэтому, вполне возможно, что если вы не нашли то, что вам нужно в существующих биндерах, вы найдёте это в SQLuaHost.
Lua — это медленно
Нам часто приходится сталкиваться с мнением, в соответствии с которым Lua — это очень медленный язык. Во-первых — это не так. Lua — это стековая машина, и там, на самом деле, просто нечему тормозить. К тому же надо понимать, что в скриптовый язык мы обычно отдаём игровую логику, бизнес-логику, а не какие-то действительно тяжёлые вещи. В результате, если Lua-скрипты заставляют игру тормозить, то проблема, возможно, кроется в неоптимальном биндинге или в нерациональном использовании каких-то функция языка. Мы, например, проводили синтетические тесты, на которых LuaJIT работает быстрее, чем Mono. При этом никто не мешает писать примерно такой вот неоптимальный код:
Здесь в каждом игровом тике создаётся новая текстура и устанавливается в качестве фона. Конечно, работать такая конструкция будет не особенно быстро, но никто не мешает писать такие вот вещи.
Lua подходит только для маленьких проектов
Следующее возражение заключается в том, что Lua сделан для того, чтобы писать какие-то маленькие вещи и что-то большое на этом языке написать невозможно. С одной стороны это правда. Но у этого языка высокая модульность. И из множества маленьких блоков можно делать достаточно большие и сложные системы. А если вспомнить то, что было уже сказано о мультипарадигменности и об ООП, то окажется, что ООП подталкивает разработчика к тому, чтобы создавать маленькие модули, которые можно использовать при создании больших и сложных конструкций.
При этом зачастую на Lua какие-то маленькие модули пишутся быстро, а в игровой индустрии «быстрее» — значит «дешевле».
Другие аргументы против Lua
Критикуя Lua, говорят о том, что язык это древний, что он, что называется, «из коробки», не поддерживает ООП, что нумерация элементов в его таблицах начинается не с 0, как можно было бы ожидать от любого приличного языка, а с 1.
Говорят, что его минус в том, что в нём нет тернарного оператора. На самом деле, таких вот аргументов против Lua довольно много, но мы не будем их обсуждать, так как полагаем, что они, по большей части, относятся к привычкам и личным предпочтениям разработчиков.
Итоги
Подведём итоги. Если ваша задача — с минимальными усилиями обзавестись встраиваемым языком — возьмите Lua. В то же время, если у вас есть время и ресурсы на разработку собственного языка или собственных биндингов — опять же — обратите внимание на Lua. Почему и в первом и во втором случаях мы рекомендуем Lua?
В первом случае, выбрав Lua, вы выберете язык, который очень просто встраивать и использовать. Существует ровно одна обучающая книга по этому языку, написанная его автором. Других книг нет просто потому, что в первой рассказано абсолютно всё, что нужно знать о Lua, и рассказывать о нём больше нечего. Lua — не идеальный и не самый распространённый в мире язык, но, по сумме критериев, это, определённо, один из лучших языков для встраивания. Он — лучший из того, что есть в нашем распоряжении прямо сейчас. К тому же, существует множество стандартных инструментов для Lua, которые сильно облегчают жизнь тем, кто им пользуется.
Во втором случае, если у вас есть ресурсы на разработку инструментов, вы, выбрав Lua, сможете с толком потратить эти ресурсы, так как Lua, несмотря на его популярность в среде разработки игр, язык весьма недооценённый. Как результат, у вас будет возможность, взяв за основу Lua, учесть свои потребности и получить именно то, что вам нужно.
Скриптовый язык
В прикладной программе, сценарий (скрипт) — это программа, которая автоматизирует некоторую задачу, которую без сценария пользователь делал бы вручную, используя интерфейс программы.
Содержание
Плагины или скрипты?
Для написания пользовательских расширений могут использоваться как скрипты (в терминологии некоторых программ «макросы»), так и плагины (независимые модули, написанные на компилируемых языках; в некоторых программах они могут называться «утилитами», «экспортёрами», «драйверами»).
Скриптовый язык удобен в следующих случаях:
У плагинов же есть три важных преимущества.
Также в плане быстродействия скриптовые языки можно разделить на языки динамического разбора (sh, command.com) и предварительно компилируемые (Perl). Языки динамического разбора считывают инструкции из файла программы минимально требующимися блоками, и исполняют эти блоки, не читая дальнейший код. Предкомпилируемые языки транслируют всю программу в байт-код и затем исполняют его. Некоторые скриптовые языки имеют возможность компиляции программы «на лету» в машинный код (т. н. JIT-компиляция).
Типы скриптовых языков
Универсальные скриптовые языки
Встроенные в прикладные программы
Командные оболочки
Встраиваемые
Также в приложение может быть встроена возможность расширения сценариями на любом из универсальных скриптовых языков, см. к примеру библиотеку SWIG или автоматический планировщик задач.
Командные файлы интерпретаторов
Многие консольные утилиты поддерживают выполнение последовательности команд, заранее записанной в файл. Такие файлы тоже называют скриптами.
Примеры таких утилит:
ChaiScript — скриптовый язык для C++
Когда возникает потребность внедрить скриптовый язык в проект на C++, первым делом большинство людей вспоминает Lua. В этой статье его не будет, я расскажу о другом, не менее удобном и легком в освоении языке под названием ChaiScript.
Небольшое предисловие
Сам я наткнулся на ChaiScript случайно, когда смотрел один из докладов Jason’а Turner’a, одного из создателей языка. Меня это заинтересовало, и в тот момент, когда нужно было выбрать скриптовый язык в проект, я решил — почему бы не попробовать ChaiScript? Результат меня приятно удивил (о моем личном опыте будет написано ближе к концу статьи), однако, как бы странно это ни звучало, на хабре не оказалось ни одной статьи, в которой хоть как-то бы упоминался этот язык, и я решил, что было бы неплохо написать о нем. У языка конечно есть документация и официальный сайт, но из наблюдений далеко не каждый будет ее читать, да и формат статьи многим (включая меня) ближе.
Сначала мы поговорим о синтаксисе языка и всех его фичах, потом о том, как внедрить его в ваш проект C++, а в конце я расскажу немного о своем опыте. Если какая-то часть вас не интересует, или вы хотите прочитать статью в другом порядке, можете воспользоваться оглавлением:
Синтаксис языка
Язык ChaiScript очень похож на C++ и JS своим синтаксисом. Прежде всего, он, как и подавляющее большинство скриптовых языков, является динамически-типизированным, однако в отличие от JavaScript, имеет строгую типизацию (никаких 1 + «2» ). Также есть встроенный сборщик мусора, язык является полностью интерпретируемым, позволяя исполнять код построчно, без компиляции в байткод. Имеет поддержку исключений (причем совместную, позволяя ловить их как внутри скрипта, так и в C++), лямбда-функции, перегрузку операторов. Не чувствителен к пробелам, позволяя писать как в одну строку через точку-с-запятой, так и в стиле python, разделяя выражения новой строкой.
Примитивные типы
ChaiScript по умолчанию хранит целочисленные переменные как int, вещественные как double, а строки с помощью std::string. Сделано это прежде всего для того, чтобы обеспечить совместимость с вызывающим кодом. В языке даже есть суффиксы у чисел, чтобы мы могли явно указать, какого типа является наша переменная:
Менять тип переменных просто так не выйдет, скорее всего вам необходимо будет определить свой оператор `=` для этих типов, иначе вы рискуете либо вызвать исключение (об этом мы поговорим позже), либо стать жертвой округления, так:
Тем не менее, вы можете объявить переменную, не присвоив ей значение, в таком случае в ней будет лежать своеобразный undefined до того момента, пока ей не будет присвоено значение.
Встроенные контейнеры
В языке предусмотрено два контейнера — Vector и Map. Работают они очень схоже с аналогами в C++ (std::vector и std::map соответственно), однако не требуют указания типа, потому что могут хранить любой. Индексировать Vector можно как обычно с помощью int’ов, а вот Map требует ключом обязательно строку. Видимо вдохновившись python, авторы также добавили возможность быстро объявлять контейнеры в коде с помощью следующего синтаксиса:
Оба этих класса почти в полной мере повторяют свои аналоги в C++, за исключением итераторов, ведь вместо них существует специальные классы Range и Const_Range. К слову, все контейнеры передаются по ссылке даже если вы пользуетесь присваиванием через =, что для меня весьма странно, так как для всех остальных типов происходит копирование по значению.
Условные конструкции
Почти все конструкции условий и циклов можно описать буквально в одном примере кода:
Контекст выполнения
Интерполяция строк
Функции и их нюансы
Функции ChaiScript — такие же объекты, как и все остальное. Их можно захватывать, присваивать переменным, делать вложенными в другие функции и передавать как аргумент. Также для них вы можете указать тип входных значений (то чего так не хватало динамически-типизированным языкам!), для этого надо указать тип перед объявлением параметра функции. Если при вызове параметр можно преобразовать в указанный, то произойдет преобразование по правилам C++, иначе генерируется исключение:
Функциям в языке также можно задавать условия вызова (call guard). Если он не соблюдается, вызывается исключение, иначе выполняется вызов. Также отмечу, что если функция не имеет return-statement’а в конце, то вернется последнее выражение. Очень удобно для небольших подпрограмм:
Классы и Dynamic_Object
ChaiScript имеет зачатки ООП, что является несомненным плюсом в случае, если вам необходимо манипулировать сложными объектами. В языке присутствует особый тип — Dynamic_Object. По факту все экземпляры классов и пространства имен являются именно Dynamic_Object с заранее заданными свойствами. Динамический объект позволяет добавлять к нему поля по ходу выполнения скрипта, а после обращаться к ним:
Классы определяются достаточно просто. Им можно задать поля, методы, конструкторы. Из интересного — через специальную функцию set_explicit(object, value) можно «зафиксировать» поля объекта, запретив добавление новых методов или атрибутов после объявления класса (обычно это делается в конструкторе):
Важный момент — по факту методы класса это лишь функции, у которых первый аргумент — объект класса с явно указанным типом. Поэтому следующий код эквивалентен добавлению метода в существующий класс:
Кто знаком с C#, может заменить, что больно похоже это на метод расширения, и будет недалек от истины. Таким образом, в языке вы можете добавить новый функционал даже для встроенных классов, к примеру для строки или int’а. Также авторы предлагают хитрый способ перегрузки операторов: чтобы его сделать, необходимо окружить символ оператора тильдой (`) как в примере ниже:
Пространства имен
Лямбда выражения и другие фичи
Лямбда выражения в ChaiScript подобны тем, что мы знаем из C++. Для них используется ключевое слово fun, и они также требуют явного указания захватываемых переменных, однако делают это всегда по ссылке. Также в языке есть функция bind, которая позволяет привязать значения к параметрам функции:
Исключения
Во время выполнения скрипта могут возникнуть исключения. Они могут быть как перехвачены в самом ChaiScript (что мы здесь и обсудим), так и в C++. Синтаксис абсолютно идентичен с плюсами, вы можете даже выкидывать число или строку:
По-хорошему, вы должны определить свой класс исключений и кидать его. О том, как его перехватывать в C++, мы поговорим во втором разделе. Для исключений интерпретатора ChaiScript генерирует свои исключения, такие как eval_error, bad_boxed_cast и т.п.
Константы интерпретатора
__LINE__ | текущая строка, если код исполняется не из файла, то ‘1’ |
__FILE__ | текущий файл, если код вызывается не из файла, то «__EVAL__» |
__CLASS__ | текущий класс или «NOT_IN_CLASS» |
__FUNC__ | текущая функция или «NOT_IN_FUNCTION» |
Перехват ошибок
Built-in функции
Внедрение в C++
Установка
ChaiScript является header-only библиотекой C++, построенной на шаблонах. Соответственно для установки вам всего лишь нужно сделать clone репозитория или просто поместить все файлы из этой папки в ваш проект. Так как в зависимости от IDE все это делается по-разному и уже давно детально расписано на форумах, далее будем предполагать, что у вас получилось подключить библиотеку, и код с include-ом: #include компилируется.
Вызов кода C++ и загрузка скрипта
Минимальный пример кода с использованием ChaiScript выглядит как показано ниже. Мы определяем простую функцию в C++, принимающую std::string и возвращающую измененную строку, а затем добавляем ссылку на нее в объект ChaiScript, чтобы в нем вызвать. Компиляция может занять значительное время, но связано это прежде всего с тем, что инстанцирование большого количества шаблонов для компилятора дело не из легких:
Надеюсь у вас получилось, и вы увидели результат выполнения функции. Сразу хочу отметить один нюанс — если вы объявите объект ChaiScript как статический, то получите неприятную ошибку времени выполнения. Связано это с тем, что язык по умолчанию поддерживает многопоточность и хранит локальные переменные потока, к которым обращается в своем деструкторе. Однако, уничтожаются они раньше, чем вызывается деструктор статического экземпляра, и в итоге мы имеем ошибку access violation или segmentation fault. Исходя из issue на github, самым простым решением будет просто поставить #define CHAISCRIPT_NO_THREADS в настройках компилятора или перед включением файла библиотеки, тем самым отключив многопоточность. Как я понял, пофиксить эту ошибку так и не удалось.
Библиотека предоставляет еще несколько методов для добавления объектов, но все они практически идентичны, поэтому рассматривать их подробно не вижу смысла. В качестве небольшой подсказки приведу код ниже:
Использование STL контейнеров
Если вы хотите передать STL-контейнеры, содержащие примитивные типы в ChaiScript, вы можете добавить инстанцирование шаблонного контейнера в ваш скрипт, чтобы не импортировать для каждого типа его методы.
С++ классы внутри ChaiScript
Возможно в рамках вашей задачи необходимо не только изменять объекты в ChaiScript, но и создавать их в скрипте. Что же, это вполне возможно. Возьмем снова для примера класс Widget и унаследуем от него класс WindowWidget, а затем добавим в скрипт возможность создавать их оба, а также конвертировать унаследованный класс в базовый:
Привязка экземпляра к методу и конвертирование типа
Для синглтон объектов удобно использовать захват ссылки на них вместе с методом или полем. В таком случае в ChaiScript мы получим либо функцию, либо глобальную переменную, к которой можно обратиться без упоминания этого объекта:
Также при экспорте более «библиотечных» классов из C++ в ChaiScript (к примеру, vec3, complex, matrix) часто требуется возможность неявного преобразования из одного типа в другой. В ChaiScript эта задача решается путем добавления type_conversion в объект скрипта. Для примера рассмотрим класс Complex и реализацию преобразования int и double в него при сложении:
Распаковка возвращаемых значений ChaiScript
Так как все объекты внутри ChaiScript хранятся с помощью shared_ptr, вы можете получить объект в виде указателя для последующей работы с ним. Для этого явно укажите тип shared_ptr при конвертации возвращаемого значения:
Главное, не стоит хранить ссылку на значение разыменнованного shared_ptr, иначе вы рискуете получить access violation после того, как переменная будет удалена в ходе автоматической сборки мусора в скрипте.
Так же как и переменные, вы можете достать из ChaiScript функции в виде запакованных функторов, захватывающих состояние объекта ChaiScript. Для примера, воспользуемся уже реализованным функционалом класса Complex и попробуем с помощью него вызвать функцию на этапе выполнения программы:
Перехват исключений из ChaiScript
Библиотеки ChaiScript
К сожалению, по умолчанию ChaiScript не предоставляет дополнительного функционала в плане библиотек. К примеру, в нем отсутствуют математические функции, хеш-таблицы, большинство алгоритмов. Часть из них вы можете скачать в виде библиотек-модулей из официального репозитория ChaiScript Extras, а затем импортировать в ваш скрипт. Для примера возьмем библиотеку math и функцию acos(x):
Вы также можете написать свою библиотеку для языка и потом импортировать. Делается это достаточно просто, поэтому советую ознакомиться с открытым кодом math или другой либы в репозиторие. В принципе, в рамках интеграции с C++ мы рассмотрели почти все, так что считаю, что раздел на этом можно закончить.
Личный опыт
В данный момент я пишу 3D-движок под OpenGL в качестве персонального проекта, и у меня возникла вполне закономерная идея реализовать консоль отладки, чтобы в реальном времени управлять состоянием приложения посредством команд. Можно было бы конечно заняться велосипедированием, но, как говорится, «игра бы не стоила свеч», поэтому я решил взять готовую библиотеку.
Как я упомянул в начале статьи, о ChaiScript к тому моменту я уже знал, поэтому передо мной стоял выбор между ним и Lua. До того момента ни с тем, ни с другим языком я знаком не был, поэтому больше всего влияли такие факторы как: понятность синтаксиса, легкость внедрения в уже существующий код и поддержка C++ вместо C, чтобы не городить забор из ООП-оберток над C-style функциями. Думаю, по ходу чтения данной статьи вы уже догадались, на что пал мой выбор.
На данный момент язык меня более чем устраивает, и писать обвязки над классами не составляет большого труда. В коде движка к запускаемому приложению прикреплен один экземпляр консоли на ImGui, в которой инициализируется объект chaiscript. Парой макросов задача о внедрении нового класса в скрипт сводится к простому описанию всех методов, которые необходимо экспортировать:
Таким же образом экспортируются еще несколько классов, а затем все соединяется вместе лямбда-функциями, объявленными прямо в коде инициализации. Результат работы скрипта вы можете увидеть на скриншоте:
консоль с chaiscript на ImGui: загрузка и установка объекта через команды
Учитывая в целом гибкость библиотеки, поменять подход к экспорту классов в скрипт не составит практически никакого труда. Безусловно, Lua обладает более обширной документацией и сообществом, и этот язык будет предпочтительней в случае, если вам нужно получить большую производительность от кода скрипта (JIT все же делает свое дело), но списывать со счетов ChaiScript все же не стоит. Если у вас есть небольшой проект, который нуждается в скриптинге, можете смело эксперементировать с доступными альтернативами.
На этой ноте я бы хотел завершить данную статью. Если вы уже имели опыт работы со скриптовыми языками внутри C++ (будь то Lua или другой язык), в комментариях буду рад услышать ваше мнение о ChaiScript и скриптингу в целом. Также я приветствую любые вопросы или замечания касательно публикации. Всем спасибо за прочтение.