Lua отличный язык для встраеваемых сценариев. Продуманная структура языка, полный арсенал для процедурного и модульного программирования, а-ля функциональные возможности в виде функций-объектов и замыканий, работа со списками, кооперативная многопотоковость, зачатки объектно-ориентированности в достаточной мере для языка подобного рода, и вообще приятный синтаксис. Написан на стандартном С, поэтому отлично компилируется на разных платформах.
Есть великое множество оберток Lua для С++, но я не нашел ни одной, где не надо вообще вызывать С-шные функции Lua вручную из основной программы. Также для создания новых функций на С++, которые можно будет вызывать из Lua, должен быть только С++‘ый подход.
Моя идея была в создании чисто плюсого интерфейса для Lua с максимально простой интеграцией в рабочий проект.
То, что пока вышло называется luascript.
Для включения в свой проект надо скопировать библиотеку в подкаталог luascript/
и добавить в проект два файла: luascript/luascript.cpp
и luascript/lua/lua-files.c
.
После этого можно писать вот такие куски кода:
lua script; script.set_variable<lua::string_arg_t>("a", "test"); script.exec("b = a .. '123';"); std::cout << script.get_variable<lua::string_arg_t>("b").value());
Данный простой скрипт принимает строку через переменню a
, добавляет к ней 123
и записывает результат в переменную b
, которая потом подхватывается из С++.
Если надо добавить свою функцию, например, для проверки существования файла, можно написать так:
class file_exists_func_t { public: // Регистрируем аргументы функции. В данном случае один аргумент типа "строка". static const lua::args_t* in_args() { lua::args_t* args = new lua::args_t(); args->add(new lua::string_arg_t()); return args; } // Регистрируем выходные параметры. В данном случае это просто bool. // Фукнция в Lua может возвращать не только одно значение, а несколько, // поэтому можно задать список типов выходных параметров. static const lua::args_t* out_args() { lua::args_t* args = new lua::args_t(); args->add(new lua::bool_arg_t()); return args; } // Задаем namespace и, собственно, имя фукнции. // Получается "fs.file_exits()". static const std::string ns() { return "fs"; } static const std::string name() { return "file_exists"; } // Данный метод вычисляет значение функции. // Сначала надо разобрать входные параметры, вычислить функцию и // положить результы с массив выходных значений. Правильность // работы с типами аргументов, выходных данных и индексов в массивах, // их описывающих, лежит на плечах автора функции. static void calc(const lua::args_t& in, lua::args_t& out) { std::string filename = dynamic_cast<lua::string_arg_t&>(*in[0]).value(); std::ifstream is(filename.c_str()); dynamic_cast<lua::bool_arg_t&>(*out[0]).value() = is.good(); } }; ... try { // Создаем исполнителя скрипта. lua script; // Регистрируем нашу функцию "fs.file_exists()". script.register_function< file_exists_func_t >(); // Устанавливаем переменную "fname" в "readme.txt". script.set_variable<lua::string_arg_t>("fname", "readme.txt"); // Вызываем скрипт. script.exec("exists = fs.file_exists(fname);"); // Получаем результат через переменную "exists". bool exists = script.get_variable<lua::bool_arg_t>("exists").value(); } catch (lua::exception& e) { std::cerr << "error: " << e.error() << ", line " << e.line(); }
Что пока не поддерживается, так это параметры типа таблица (хеш) для передачи их в функцию и получения их в качестве результата.
В каталоге lib
лежат несколько мини примеров на Lua. Например, вот так можно вызвать внешнюю функцию для base64
кодирования или декодирования:
lua script; script.exec("package.path = package.path .. ';./lib/?.lua'"); script.exec("require('base64'); a = base64.encode('test');"); // Данный пример напечатает "dGVzdA==". std::cout << script.get_variable<lua::string_arg_t>("a").value();
Исходники доступны для просмотра в онлайне, или через Mercurial.
Сборка.
Пока я проверял только в Студии 2008. Тестовый проект включает в себя библиотеку, lua 5.1.4, Google Test 1.3.0 и несколько тестов, чтобы почувствовать вкус библиотеки. Все в одном флаконе.
Те, у кого есть SCons, могут собрать, набрав scons -Q
. У кого нет, могут запустить скрипт compile-vs2008.cmd
. Собранный runner для тестов luascript_unittest_vs2008.exe
должен работать без ошибок. Посмотрев сами тесты в файле luascript_unittest.cpp
можно в целом понять, как работать с библиотекой. Документация, конечно, будет, но пока так.
Общие замечания.
Забавно, в этих исходниках я попытался в качестве эксперимента максимально работать по стандарту кодирования Google. Из основного, что затронуло лично меня, это:
public
, protected
, private
отступ в один пробел.{
практически всегда на той же строке (для классов, функций, циклов, условий и т.д.). Я раньше так не делал для классов и функций.cast
‘ов в стиле С, даже для элементарных типов. Только приведения в стиле С++. Мне это очень нравится.Это был снова эксперимент на Google Code и в opensource’e в целом. Если честно, то выкладывание исходников на публику страшно оздоравливает код, причем по всем статьям.
Данный проект не такой сухой как ранее выложенный SerialCom. Я с ним более менее активно работаю, так что должны быть точно улучшения. На работе, например, я его примастырил для гибко сконфирурированного фильтрования при журналировании. Есть, конечно, проблемы с производительностью (интерпретатор, все-таки, хоть и с виртуальной машиной), но есть пути улучшения.
Посты и ссылки по теме: