Интеграция Lua в C++
Lua — это высокоуровневый мультипарадигменный язык программирования, который в основном используется во встраиваемых приложениях, а также обеспечивает мощную поддержку сценариев для существующих продуктов. Например, улучшение сценариев для NGINX, HA Proxy, Wireshark и т. д. Еще одна важная область, в которой Lua нашел применение, — это фреймворки игрового движка.
Зачем использовать Lua?
Существуют различные причины выбора Lua для обеспечения поддержки сценариев:
- Его небольшой размер и превосходная скорость по сравнению с другими средами сценариев.
- В версии 2.0 в Lua была проведена серьезная реструктуризация, которая привела к значительному увеличению производительности.
- Кроме того, Lua предоставляет JIT-компилятор, очень быстрый и занимающий очень мало места.
- По сравнению с другими популярными платформами сценариев (Python), Lua использует гораздо меньший объем памяти.
- Lua имеет простой, но очень эффективный синтаксис.
- Он имеет встроенную поддержку передачи анонимных функций в качестве аргументов, сопоставления/массива как одной концепции, называемой «таблицей», метафункций и метатаблиц, что позволяет реализовывать различные парадигмы программирования и т. д.
Несмотря на все вышеперечисленные преимущества, Lua предоставляет очень низкоуровневый C API , который требует от разработчика изучения внутреннего устройства механизма Lua, прежде чем он сможет использовать его в приложениях. Однако это изменилось с библиотекой Lua Cpp .
LuaCpp: это легкая оболочка для API-интерфейсов Lua C, которая обеспечивает доступ к библиотеке Lua на двух уровнях, т. е. доступ через API-интерфейсы высокого уровня, которые скрывают сложность API-интерфейсов C и движка, и доступ к низкоуровневому Lua. API.
Как установить Луа?
LuaCpp можно установить как общесистемную библиотеку или как подмодуль вашего существующего проекта. Запустите приведенную ниже команду, чтобы установить LuaCpp в Ubuntu.
- Клонируйте библиотеку LuaCpp по ссылке ниже:
=> git clone https://github.com/jordanvrtanoski/luacpp.git
- Измените каталог на luacpp, создайте новый каталог как сборку и измените каталог для повторной сборки, используя следующие команды:
=> cd luacpp
=> mkdir build
=> cd build
- Теперь создайте источник, используя следующие команды:
=> cmake ../Source
=> make -j `nproc`
- Теперь установите библиотеку с помощью следующей команды:
=> make install
После установки библиотеки соберите файл следующим образом:
=> gcc hello.cpp -I /usr/local/include/LuaCpp -I /usr/include/lua5.3/ -lluacpp -llua5.3 -lstdc++ -o hello
Выведите файл как:
=> hello
Ниже приведена та же программа, чтобы проиллюстрировать то же самое:
C++
// C++ program to illustrate the use of // LuaCpp library #include <LuaCpp.hpp> #include <iostream> using namespace LuaCpp::Registry; using namespace std; // Driver Code int main( int argc, char ** argv) { cout << "Hi from C++, this is a demo" << " how LuaCpp can be used
" ; LuaContext lua; // The simples way is to use // CompileStringAndRun method try { lua.CompileStringAndRun( "print("The fastest way to " "start using lua in " "a project")" ); } catch (std::runtime_error& e) { std::cout << e.what() << "
" ; } } |
Выход:
Передача данных из C++ в Lua и обратно:
Пример показывает нам, как скомпилировать и выполнить фрагмент кода Lua из C++. Однако без возможности передавать данные из C++ в Lua и обратно из Lua в C++ не так много реальных случаев, которые можно решить с помощью этого шаблона.
LuaCpp прибывает готовым установить мост между двумя средами выполнения с минимальным знанием внутренней работы Lua, а также с минимальным кодом. Давайте улучшим пример «Hello World» , добавив переменную, которая будет использоваться обеими средами выполнения. Это вводит переменную « String » с именем « world » и заполняет ее значением из контекста C++ . Внутри контекста Lua обновите значение переменной, а по возвращении в контекст C++ напечатайте значение переменной.
Ниже приведена программа, иллюстрирующая то же самое:
C++
// C++ program to illustrate the // above approach #include <LuaCpp.hpp> #include <iostream> using namespace LuaCpp; using namespace LuaCpp::Registry; using namespace LuaCpp::Engine; using namespace std; // Driver Code int main( int argc, char ** argv) { LuaContext ctx; shared_ptr<Engine::LuaTString> str = make_shared<Engine::LuaTString>( "world from C++!" ); ctx.AddGlobalVariable( "world" , str)); ctx.CompileString( "test" , "print("Hello "..world)" "world = "world from lua!"" ); // Try Catch Block try { ctx.Run( "test" ); } catch (runtime_error& e) { cout << e.what() << "
" ; } cout << "Hello " << str->getValue() << "
" ; } |
Выход:
Контекст позволяет передавать несколько переменных из области C++ в область Lua и наоборот. Приведенный выше шаблон позволяет добавить поддержку сценариев в проект C++ в большинстве случаев. Простой 4-этапный процесс:
- Создайте контекст.
- Создайте общие переменные и зарегистрируйте их в контексте.
- Скомпилируйте сценарий Lua (из строки, файла или нескольких файлов в папке).
- Запустите скрипт.
Поддерживаемые типы Lua:
LuaCpp предоставляет следующие типы переменных, которые можно передавать между контекстом C++ и Lua:
- «LuaTString»: эквивалент «std::string» в C++.
- «LuaTNumber»: эквивалент «двойного» в C++. Lua позволяет компилировать LUA_TNUMBER (внутренний тип числа Lua) как число с плавающей запятой, однако LuaCpp будет представляться в контексте C++ как двойное, что означает, что в случаях, когда библиотека Lua настроена для определения числа как число с плавающей точкой может быть потеря данных из-за точности.
- «LuaTBoolean»: эквивалент « bool » в C++.
- «LuaTNil»: нулевой тип, который используется движком Lua для обозначения отсутствия значения.
- «LuaTTable»: гибрид массива/карты, который в C++ реализован как «std::map». Карта может иметь строку или число в качестве ключа, и есть особый случай, когда все ключи в карте имеют тип число, карта представляет собой массив. Это следует логике реализации Lua Table.
- «LuaTUserData»: специальный тип, который позволяет реализовать пользовательские типы. Это очень мощный тип, и тип LuaMetaObject движка реализован на основе этого примитивного типа. Это понятие заслуживает отдельной статьи.
Вывод:
Добавление поддержки сценариев в существующий проект C++ обеспечивает огромную гибкость и настраиваемость разработанного приложения. Хотя API-интерфейсы Lua C не очень сложны, они по-прежнему требуют от разработчика полного понимания внутренней работы виртуальной машины Lua. Как описано в этой статье, LuaCpp абстрагируется от всей этой сложности и предоставляет интерфейс, хорошо знакомый разработчику C++.