Есть великое множество оберток 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. Из основного, что затронуло лично меня, это:
- Отступ в 2 пробела (естественно, никаких табов). Для слов "public", "protected", "private" отступ в один пробел.
- Максимальная экономия вертикального места (по возможности не лепить лишних пустых строк).
- Открывающая скобка "{" практически всегда на той же строке (для классов, функций, циклов, условий и т.д.). Я раньше так не делал для классов и функций.
- Никаких cast'ов в стиле С, даже для элементарных типов. Только приведения в стиле С++. Мне это очень нравится.
- Забота о длинных строках. Как только можно избегать строк длинее 80 символов.
- В именах закрытых членов класса использовать не "__" в качестве префикса, а "_" в качестве суффикса.
Данный проект не такой сухой как ранее выложенный SerialCom. Я с ним более менее активно работаю, так что должны быть точно улучшения. На работе, например, я его примастырил для гибко сконфирурированного фильтрования при журналировании. Есть, конечно, проблемы с производительностью (интерпретатор, все-таки, хоть и с виртуальной машиной), но есть пути улучшения.
Посты и ссылки по теме:
Хм.... А чем Luabind не устроил? Если ответ - "зависимость от Boost" - то от Буста он не более пары хедеров тянет и все. Пользуюсь и очень доволен.
ОтветитьУдалитьТочно так, ибо когда в списке поддержки обязательно стоят HP-UX, AIX да Tru64, причем с не самыми новыми компиляторами, с бустом бывает туго, хотя, конечно, luabind находится на ином уровне, чем мой простецкий luascript.
ОтветитьУдалитьА как насчет SWIG?
ОтветитьУдалитьСпасибо за наводку. SWIG до этого не видел, надо будет посмотреть.
ОтветитьУдалитьОбычно же как -- нужно что-то и сразу хочется написать свою компактную и понятную реализацию, поэтому и по сторонам не всегда смотришь.
> Данный проект не такой сухой как ранее выложенный SerialCom. Я с ним более менее активно работаю, так что должны быть точно улучшения. На работе, например, я его примастырил для гибко сконфирурированного фильтрования при журналировании. Есть, конечно, проблемы с производительностью (интерпретатор, все-таки, хоть и с виртуальной машиной), но есть пути улучшения.
ОтветитьУдалитьА что по поводу LuaJIT? http://luajit.org/index.html
Сам не использовал, но судя по тестам, выигрыш должен быт значительный.
Про LuaJIT я в курсе. Мы тут используем Lua для парсинга FIX протокола налету, чтобы не хардкодить под каждый случай, но пока чистый Lua, без JIT. Поглядим, как пойдет.
ОтветитьУдалить