Интеграция Lua в C++

Опубликовано: 14 Сентября, 2022

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++.

C++