Lua. Выгрузить свечи в файл.
Суть проста- я хотел бы чтобы LUA скрипт каждый день мне выгружал свечи минутки по нужному инструменту в файл. И "дозаписывал" этот файл.
Идеально было бы например в Excel или базу данных.
Но для начала хотя бы в txt файл.
Плюс к этому было бы хорошо, чтобы он и внутри себя имел массив свечей минуток. Ну т.е нажал кнопку "загрузить", и дальше можно внутри скрипта увидеть таблицу минуток и с ней работать.
Пока не могу понять с какой стороны подойти к этому вопросу.
Хотя бы как из Quik в LUA забрать значения свечек минуток сразу?
2 Ответ от kalikazandr 2015-04-29 18:16:12 (2015-04-29 18:26:25 отредактировано kalikazandr)
- Зарегистрирован: 2014-09-10
- Сообщений: 352
Re: Lua. Выгрузить свечи в файл.
Хотя бы как из Quik в LUA забрать значения свечек минуток сразу?
local n = getNumCandles(ind)—кол-во свечек, где ind = идентификатор графика
local t, res, _ = getCandlesByIndex (ind, 0, 0, n)—получаем все свечи
или так:
local t, res, _ = getCandlesByIndex (ind, 0, n — 500, 500)—получить последние 500 свечей (для справки)
—t — таблица со свечками, res — длина таблицы, _ — легенда (подпись) графика
—t[0] — первая свеча
—t[res-1] — последняя свеча
если проделать такой трюк:
t[0] = nil,
то получим обычный массив Lua и скорость работы с таблицей чуть увеличится, но вы этого не заметите ))
3 Ответ от slkumax 2015-04-29 18:28:52
- Зарегистрирован: 2013-06-13
- Сообщений: 68
Re: Lua. Выгрузить свечи в файл.
Хотя бы как из Quik в LUA забрать значения свечек минуток сразу?
local n = getNumCandles(ind)—кол-во свечек, где ind = идентификатор графика
local t, res, _ = getCandlesByIndex (ind, 0, 0, n)—получаем все свечи
или так:
local t, res, _ = getCandlesByIndex (ind, 0, n — 500, 500)—получить последние 500 свечей (для справки)
—t — таблица со свечками, res — длина таблицы, _ — легенда (подпись) графика
—t[0] — первая свеча
—t[res-1] — последняя свеча
если проделать такой трюк:
t[0] = nil,
то получим обычный массив Lua и скорость работы с таблицей чуть увеличится, но вы этого не заметите ))
Спасибо большое. А какой формат этой таблицы свечек?
Я на данный момент реализовал вот таким способом:
function BazToGrZap()
Baz = CreateDataSource(CLASS, SEC, INTERVAL_M1)
Raz=Baz:Size()
for is=1, Raz do
Open=Baz:O(is)
Hight=Baz:H(is)
Close=Baz:C(is)
Low=Baz:L(is)
Day=Baz:T(is).day
Month=Baz:T(is).month
Year=Baz:T(is).year
DateTime=
gridBaza:SetCell(2,is,SEC)
gridBaza:SetCell(3,is,Open)
gridBaza:SetCell(4,is,Hight)
gridBaza:SetCell(5,is,Low)
gridBaza:SetCell(6,is,Close)
gridBaza:SetCell(0,is,Day)
end
И пока не понимаю как "суммировать год, месяц, день и часы, минуты и секунды, чтобы представить их в двух столбцах таблицы.
Очень подробно разжёвано для чайников по LUA часть1!

Написанный на Lua скрипт не имеет какой-либо специальной функции, с которой начиналось бы его выполнение. Скрипт можно рассматривать просто как набор команд (инструкций), который выполняется, начиная с первой инструкции.
Скрипт может быть как очень простым, состоящим всего из одной команды, так и весьма сложным, содержащим десятки, сотни и даже тысячи инструкций. Следующие друг за другом инструкции могут разделяться точкой с запятой (;). Однако это требование не является обязательным, поэтому весь приведённый ниже код является корректным с точки зрения синтаксиса:
Работа с переменными в Lua
Переменные используются для хранения значений в процессе выполнения скрипта.
Имена переменных в Lua
Именами (идентификаторами) переменных в Lua могут быть любые последовательности из букв, цифр и символа подчеркивания, начинающиеся не с цифры.
Язык Lua различает регистр символов, поэтому abc, Abc, ABC являются различными именами.
В таблице ниже приведены слова, которые зарезервированы языком Lua и не могут использоваться в именах переменных:
and break do else elseif
end false for function if
in local nil not or
repeat return then true until
Кроме того, все имена, начинающиеся с символа подчеркивания, за которым идут заглавные буквы (например, _VERSION) также являются зарезервированными.
Какие переменные бывают в Lua?
Переменные в Lua могут быть глобальными и локальными. Если переменная не объявлена явно как локальная, она считается глобальной.
Глобальные переменные Lua
Глобальная переменная появляется в момент присваивания ей первого значения. До присваивания первого значения обращение к глобальной переменной даёт nil.
MsgBox(tostring (g)) —> nil
MsgBox(tostring (g)) —> 1
Глобальная переменная существует до тех пор, пока существует среда исполнения скрипта и доступна любому Lua-коду, выполняемому в этой среде.
При необходимости удалить глобальную переменную можно явным образом, просто присвоив ей значение nil.
g = 1 — создаем глобальную переменную g со значением 1
g = nil — удаляем глобальную переменную g
MsgBox(tostring (g)) —> nil
Все глобальные переменные являются полями обычной таблицы, называемой глобальным окружением. Эта таблица доступна через глобальную переменную _G. Поскольку полями глобального окружения являются все глобальные переменные (включая саму _G), то _G._G == _G.
Локальные переменные Lua
Любые локальные переменные должны быть объявлены явно с использованием ключевого слова local. Объявить локальную переменную можно в любом месте скрипта. Объявление может включать в себя присваивание переменной начального значения. Если значение не присвоено, переменная содержит nil.
local a — объявляем локальную переменную a
local b = 1 — объявляем локальную переменную b, присваиваем ей значение 1
local c, d = 2, 3 — объявляем локальные переменные c и d, присваиваем им значения 2 и 3
Область видимости локальной переменной начинается после объявления и продолжается до конца блока.
Областью видимости переменной называется участок кода программы, в пределах которого можно получить доступ к значению, хранящемуся в данной переменной.
Под блоком понимается:
тело управляющей конструкции (if-then, else, for, while, repeat);
фрагмент кода, заключённый в ключевые слова do. end.
Если локальная переменная определена вне какого-либо блока, её область видимости распространяется до конца скрипта.
a = 5 — глобальная переменная a
local i = 1 — переменная i локальна в пределах скрипта
while i <= a do — цикл от 1 до 5
local a = i^2 — переменная а локальна внутри цикла while
MsgBox(a) —> 1, 4, 9, 16, 25
MsgBox(a) —> 5 (здесь обращение к глобальной a)
local a — переменная а локальна внутри then
MsgBox(a) —> 5 (здесь обращение к глобальной a)
local a = 20 — переменная а локальна внутри do-end
MsgBox(a) —> 5 (здесь обращение к глобальной a)
Когда возможно, рекомендуется использовать локальные переменные вместо глобальных. Это позволит избежать «засорения» глобального пространства имён и обеспечит лучшую производительность (поскольку доступ к локальным переменным в Lua выполняется несколько быстрее, чем к глобальным).
Типы данных Lua
Какие типы данных поддерживает язык Lua?
Lua поддерживает следующие типы данных:
1. Nil (ничего). Соответствует отсутствию у переменной значения. Этот тип представлен единственным значением — nil.
2. Boolean (логический). К данному типу относятся значения false (ложь) и true (истина).
При выполнении логических операций значение nil рассматривается как false. Все остальные значения, включая число 0 и пустую строку, рассматриваются как true.
3. Number (числовой). Служит для представления числовых значений.
В числовых константах можно указывать необязательную дробную часть и необязательный десятичный порядок, задаваемый символами «e» или «E». Целочисленные числовые константы можно задавать в шестнадцатеричной системе, используя префикс 0x.
Примеры допустимых числовых констант: 3, 3.0, 3.1415926, 314.16e-2, 0xff.
4. String (строковый). Служит для представления строк.
Строковые значения задаются в виде последовательности символов, заключённой в одинарные или двойные кавычки:
b = ‘это вторая строка’
Строки, заключённые в двойные кавычки, могут интерпретировать C-подобные управляющие последовательности (escape-последовательности), начинающиеся с символа «\» (обратный слэш):
\n (перевод строки),
\r (возврат каретки);
\t (горизонтальная табуляция),
Символ в строке также может быть представлен своим кодом с помощью escape-последовательности:
где ddd — последовательность из не более чем трёх цифр.
Кроме кавычек для определения строки могут также использоваться двойные квадратные скобки:
local a = [[Компания «Кронос»]]
Определение строки с помощью двойных квадратных скобок позволяет игнорировать все escape-последовательности, т. е. строка создаётся полностью так, как описана:
local a = [[string
При определении строки с помощью двойных квадратных скобок учитываются символы табуляции и переноса.
Двойные скобки могут быть вложенными. Для того чтобы их не перепутать, между скобками вставляется символ«равно» (=):
local a = [=[определение строки [[string]] в Lua]=]
— будет срока: «определение строки [[string]] в Lua»
5. Function (функция). Функции в Lua могут быть записаны в переменные, переданы как параметры в другие функции ивозвращены как результат выполнения функций.
6. Table (таблица). Таблица представляет собой набор пар «ключ» — «значение», которые называют полями илиэлементами таблицы. Как ключи, так и значения полей таблицы могут иметь любой тип, за исключением nil. Таблицы не имеют фиксированного размера: в любой момент времени в них можно добавить произвольное число элементов.
Подробнее — в статье «Создание таблиц в Lua»
7. Userdata (пользовательские данные). Является особым типом данных. Значения этого типа не могут быть созданы или изменены непосредственно в Lua-скрипте.
Userdata используется для представления новых типов, созданных в вызывающей скрипт программе или в библиотеках, написанных на языке С. Например, библиотеки расширений Lua для «CronosPRO» используют этот тип для представления таких объектов, как:
банки данных (класс Bank);
базы данных (класс Base);
записи (класс Record) и т. п.
8. Thread (поток). Соответствует потоку выполнения. Эти потоки никаким образом не связаны с операционной системой и поддерживаются исключительно средствами самого Lua.
Как в Lua задать тип переменной?
Lua не предусматривает явного задания типа переменной. Тип переменной устанавливается в момент присвоения переменной значения. Любой переменной может быть присвоено значение любого типа (вне зависимости от того, значение какого типа она содержала ранее).
a = 123 — переменная a имеет тип number
a = «123» — теперь переменная a имеет тип string
a = true — теперь переменная a имеет тип boolean
a = <> — теперь переменная a имеет тип table
Переменные типа table, function, thread и userdata не содержат самих данных, а хранят ссылки на соответствующие объекты. При присваивании, передачи в функцию в качестве аргумента и возвращении из функции в качестве результата копирования объектов не происходит, копируются только ссылки на них.
a = <> — создаем таблицу. В переменную a помещается ссылка на таблицу
b = a — переменная b ссылается на ту же таблицу, что и a
a[1] = 10 — элементу таблицы с индексом 1 присвоено значение 10
Остальные данные являются непосредственными значениями.
Как в Lua получить тип переменной?
Тип значения, сохранённого в переменной, можно выяснить при помощи стандартной функции type. Эта функция возвращает строку, содержащую название типа («nil», «number», «string», «boolean», «table», «function», «thread», «userdata»).
t = type («это строка») — t равно «string»
t = type (123) — t равно «number»
t = type (type) — t равно «function»
t = type (true) — t равно «boolean»
t = type (nil) — t равно «nil»
t = type (CroApp.GetBank()) — t равно «userdata»
Как в Lua преобразовать тип переменной?
Lua при необходимости автоматически преобразует числа в строки и наоборот. Например, если строковое значение является операндом в арифметической операции, оно преобразуется в число. Аналогично числовое значение, встретившееся в том месте, где ожидается строковое, будет преобразовано в строку.
a = «10» + 2 — a равно 12
a = «10» + 2 — a равно «10 + 2»
a = "-5.3e-10"*«2» — a равно -1.06e-09
a = «строка» + 2 — Ошибка! Невозможно преобразовать «строка» в число
Значение любого типа можно явным образом преобразовать в строку с помощью стандартной функции tostring.
a = tostring (10) — a равно «10»
a = tostring (true) — a равно «true»
a = tostring (nil) — a равно «nil»
a = tostring (<[1] = «это поле 1»>) — a равно «table: 06DB1058»
Из предыдущего примера видно, что содержимое таблиц функцией tostring не преобразуется. Выполнить такое преобразование можно с помощью функции render.
a = render (10) — a равно «10»
a = render (true) — a равно «true»
a = render (nil) — a равно «nil»
Для явного преобразования значения в число можно использовать стандартную функцию tonumber. Если значение является строкой, которую можно преобразовать в число (или уже является числом), функция возвращает результат преобразования, в противном случае возвращает nil.
a = tonumber («10») — a равно «10»
a = tonumber («10»..".5") — a равно 10.5
a = tonumber (true) — a равно «nil»
a = tonumber (nil) — a равно «nil»
Расстановка комментариев в Lua
Комментарий в Lua начинается двумя знаками «минус» (—) и продолжается до конца строки.
local a = 1 — однострочный комментарий
Если непосредственно после символов «—» идут две открывающие квадратные скобки ([[), комментарий являетсямногострочным и продолжается до двух закрывающих квадратных скобок (]]).
local a = 1 — [[ многострочный
Двойные скобки в комментариях могут быть вложенными. Для того чтобы их не перепутать, между скобками вставляется знак равенства (=):
local a = [[Компания «Кронос»]] — [=[
local a = [[Компания «Кронос»]]
Количество символов «=» определяет вложенность:
local a = [=[определение некоторой строки [[string]] в языке Lua]=] —[==[
local a = [=[определение некоторой строки [[string]] в языке Lua]=]
Операции, применяемые в Lua
В выражениях, написанных на Lua, могут применяться следующие виды операций:
1. Арифметические операции.
Lua поддерживает следующие арифметические операции:
^ (возведение в степень);
% (остаток от деления).
Арифметические операции применимы как к числам, так и к строкам, которые в этом случае преобразуются в числа.
2. Операции сравнения.
В Lua допустимы следующие операции сравнения величин:
<= (меньше или равно);
>= (больше или равно).
Операции сравнения всегда возвращают логическое значение true или false.
Правила преобразования чисел в строки (и наоборот) при сравнениях не работают, т. е. выражение «0» == 0 даёт в результате false.
3. Логические операции.
К логическим операциям относятся:
and (логическое И).
Операция and возвращает свой первый операнд, если он имеет значение false или nil. В противном случае, операция возвращает второй операнд (причём этот операнд может быть произвольного типа).
a = (nil and 5) — a равно nil
a == (false and 5) — a равно false
a == (4 and 5) — a равно 5
or (логическое ИЛИ).
Операция or возвращает первый операнд, если он не false и не nil, иначе он возвращает второй операнд.
a == (4 or 5) — a равно 4
a == (false or 5) — a равно 5
Логические операции and и or могут возвращать значения любых типов.
Логические операции and и or вычисляют значение второго операнда только в том случае, если его нужно вернуть. Если этого не требуется, второй операнд не вычисляется. Например:
a == (4 or f()) — вызова функции f() не произойдет
not (логическое НЕ).
Операция not всегда возвращает true или false.
4. Операция конкатенации.
Для конкатенации (объединения) строк служит операция… (две точки).
a = «Кронос».."-"..«Информ» — переменная a получит значение «Кронос-Информ»
Если один или оба операнда являются числами, выполняется их преобразование в строки.
a = 0..1 — переменная a получит значение «01»
5. Операция получения длины.
В Lua определена операция длины #, которую можно использовать для получения длины строки.
len = #a — len равно 6
len = #«ещё строка» — len равно 10
С помощью операции # можно также узнать максимальный индекс (или размер) массива. Подробнее — в статье «Работа с массивами в Lua» .
Приоритет операций в Lua
В языке Lua выполнение операций осуществляется в соответствии со следующим приоритетом (в порядке убывания):
2. not # — (унарный)
Вызов скриптов из форм
С каждой формой (включая вложенные формы) связан отдельный скрипт, который обычно содержит функции, выполняющие обработку событий формы и её элементов.
Когда форма запускается, её скрипт загружается в глобальное окружение. При возникновении события формы или её элемента система вызывает сопоставленную этому событию функцию-обработчик.
Необходимо отметить, что скрипт формы, хотя и не содержит вызова функции module, фактически является модулем. Это означает, что переменные, объявленные в скрипте формы без ключевого слова local, не выносятся в глобальное окружение и доступны только внутри этого скрипта. Если необходимо сделать какое-либо значение доступным для скриптов других форм, его следует явным образом определить в глобальной таблице _G:
Другой скрипт форм сможет прочитать это значение следующим образом:
Блоки операторов (инструкций)
К основным операторам Lua относятся:
операторы для организации циклов.
Группа операторов может быть объединена в блок (составной оператор) при помощи конструкции do… end.
do — начало блока
<оператор1> — тело блока
end — конец блока
Блок открывает новую область видимости, в которой можно определять локальные переменные.
a = 5 — глобальная переменная a
local a = 20 — внутри do-end определяется локальная переменная а
MsgBox(a) —> 5 (здесь обращение уже к глобальной a)
Оператор присваивания в Lua
Присваивание изменяет значение переменной или поля таблицы. В простейшем виде присваивание может выглядеть так:
a = 1 — переменной a присвоено значение 1
a = b + c — переменной a присвоена сумма значений переменных b и с
a = f(x) — переменной a присвоено значение, возвращённое функцией f(x)
В Lua допускается так называемое множественное присваивание, когда несколько переменных, находящихся слева от оператора присваивания, получают значения нескольких выражений, записанных справа от оператора присваивания:
a, b = 1, 5*c — a равно 1; b равно 5*c
Если переменных больше чем значений, «лишним» переменным присваивается nil.
a, b, c = 1, 2 — a равно 1; b равно 2; c равно nil
Если значений больше чем переменных, «лишние» значения игнорируются.
a, b = 1, 2, 3 — a равно 1; b равно 2; значение 3 не использовано
Множественное присваивание можно использовать для обмена значениями между переменными:
a = 10; b = 20 — a равно 10, b равно 20
a, b = b, a — теперь a равно 20, b равно 10
Условный оператор (if) в Lua
Оператор if проверяет истинность заданного условия. Если условие является истинным, выполняется часть кода, следующая за ключевым словом then (секция then). В противном случае, выполняется код, следующий за ключевым словом else (секция else).
return a — если a больше b, вернуть a
return b — в противном случае — вернуть b
Секция else является необязательной.
a = 0 — если a меньше 0, присвоить a значение 0
Вместо вложенных операторов if можно использовать конструкцию elseif. Например, приведенный код:
return «Иван» — если a равно 1
return «Петр» — если a равно 2
return «Сергей» — если a равно 3
return «Нет такого игрока» — если a — ни одно из перечисленных
будет проще для восприятия, если заменить его следующим:
return «Иван» — если a равно 1
elseif a == 2 then
return «Петр» — если a равно 2
elseif a == 3 then
return «Сергей» — если a равно 3
return «Нет такого игрока» — если a — ни одно из перечисленных
Цикл с предусловием (while) в Lua
Оператор while предназначен для организации циклов с предусловием и имеет следующий вид:
while <condition> do
Перед каждой итерацией цикла проверяется условие <condition>:
если условие ложно, цикл завершается и управление передаётся первому оператору, следующему за оператором while;
если условие истинно, выполняется тело цикла, после чего все действия повторяются.
while i > 0 do — цикл от 10 до 1
Для выхода из цикла до его завершения можно использовать оператор break.
while i > 0 do — ищем в массиве отрицательное значение
if a[i] < 0 then break end — если найдено, прерываем цикл
i = i — 1 — иначе переходим к следующему элементу
MsgBox («Индекс отрицательного значения: »..i)
MsgBox («Массив не содержит отрицательных значений»)
Подробнее об особенностях использования оператора break — в статье «Операторы break и return»
Цикл с постусловием (repeat) в Lua
Оператор repeat предназначен для организации циклов с постусловием и имеет следующий вид:
Тело цикла выполняется до тех пор, пока условие <condition> не станет истинным. Проверка условия осуществляется после выполнения тела цикла, поэтому в любом случае тело цикла выполнится хотя бы один раз.
— суммируем значения массива a, пока сумма не превысит 10
MsgBox («Сложено »..i.." элементов. Сумма равна "..sum)
Для выхода из цикла до его завершения можно использовать оператор break.
Подробнее об особенностях использования оператора break — в статье «Операторы break и return»
Циклы с оператором for в Lua
Оператор for предназначен для организации циклов и допускает две формы записи:
простую (числовой for);
расширенную (универсальный for).
Простая форма оператора for
Простая форма оператора for имеет следующий вид:
for var = exp1, exp2, exp3 do
Тело цикла выполняется для каждого значения переменной цикла (счётчика) var в интервале от exp1 до exp2, с шагом exp3.
Шаг может не задаваться. В этом случае он принимается равным 1.
for i = 1, 10 do — цикл от 1 до 10 с шагом 1
MsgBox («i равно »..i)
for i = 10, 1, -1 do — цикл от 10 до 1 с шагом -1
MsgBox («i равно »..i)
Выражения exp1, exp2 и exp3 вычисляются всего один раз, перед началом цикла. Так, в примере ниже, функция f(x) будет вызвана для вычисления верхнего предела цикла только один раз:
for i = 1, f(x) do — цикл от 1 до значения, возвращенного функцией f()
MsgBox («i равно »..i)
Переменная цикла является локальной для оператора цикла и по его окончании не определена.
for i = 1, 10 do — цикл от 1 до значения, возвращенного функцией f()
MsgBox («i равно »..i)
MsgBox («После выхода из цикла i равно »..i) — Неверно! i равно nil
Значение переменной цикла нельзя изменять внутри цикла: последствия такого изменения непредсказуемы.
Для выхода из цикла до его завершения используется оператор break.
for i = 1,#a do — ищем в массиве отрицательное значение
if a[i] < 0 then — если найдено.
index = i — сохраняем индекс найденного значения.
break — и прерываем цикл
MsgBox («Индекс отрицательного значения: »..index)
Подробнее об особенностях использования оператора break — в статье «Операторы break и return» )
Язык Lua и использование скриптов на нем в программах на С++.
Во многих случаях программам требуется более высокая степень гибкости, чем та, которая дается простым изменением конфигурационных параметров. Примером этого может быть необходимость изменения логики системы. И такие изменения желательно сделать простыми, удобными и не требующими перекомпиляции всей системы.
В подобных случаях крайне удобным является использование скриптовых языков — те элементы функциональности, которые нужно легко/быстро изменять пишутся на специальном языке (обычно не компилируемом) и вызываются непосредственно из самой системы. При этом для изменения логики достаточно просто изменить соответствующую программу на этом языке (скрипт) — обычно это просто текстовый файл. Причем такое изменение может осуществляться даже во время работы системы.
В качестве языка, используемого для написания скриптов, можно использовать как традиционные языки (Python, Ruby, Perl), так и специализированные языки, такие как Lua или Squirell.
Последние, хотя и уступают традиционным в гибкости и наборе библиотек, являются зато гораздо более «легковесными» — они требуют всего лишь наличия соответствующей dll-ки, отличаются большим быстродействием и меньшими требованиями к памяти. Кроме того они достаточно просты.
Далее мы рассмотрим скриптовый язык Lua и использование скриптов на нем в программах на С++.
Язык Lua пришел к нам из Бразилии (именно оттуда где в лесах много диких обезьян :))))). Вначале у его авторов было два небольших скриптовых языка для работы и визуализации данных — DEL (Data Entry Language) и SOL (Simple Object Language). Каждый из них был создан под свою задачу и они оба очень хорошо справлялись со своими задачами и поэтому стали возникать пожелания добавить к ним новые возможности.
В какой то момент стало ясно, что на самом деле их можно объединить в один язык, для которого было предложено название Lua — по португальски Sol — Солнце, а Lua — Луна.
Так в 1994 году и появился язык Lua. При этом с самого начала была цель, чтобы этот язык мог идти практически на любой из используемых тогда платформ, включая MS DOS, Windows, Unix и Mac OS.
На официальном сайте www.lua.org можно найти краткую историю языка — откуда он произошел, для чего использовался и как развивался.
Одним из существенных отличий языка Lua является то, что этот язык предназначен именно для использования в качестве скриптового языка, программы на котором будут вызываться из основной программы (в отличии от языков Python/Ruby/Perl, которые могут использоваться и самостоятельно).
Сам язык (точнее библиотека, обеспечивающая возможность загрузки и выполнения скриптов на нем) реализован на «чистом» С и легко компилируется и работает практически под любой операционной системой — так для работы под форточками требуется всего лишь наличие файла lua51.dll (для Lua версии 5.1).
Lua используется во многих известных играх, таких как FarCry, STALKER и другие. Именно использование скриптов на Lua позволило создать большое количество модов для игры STALKER, сильно меняющих саму логику игры и добавляющих в нее ряд новых возможностей.
Также существует и JIT-компилятор для Lua (который и был использован в игре STALKER) — LuaJIT.
Далее мы сначала рассмотрим установку Lua, потом сам язык, а потом уже его интеграция с С++.
Есть очень хороший русскоязычный сайт, полностью посвященный языку Lua — ilovelua.narod.ru. На этом сайте Вы найдете много интересных статей и примеров. Также есть хорошая вводная статья на сайте www.gamedev.ru.
Ниже приводится пример скрипта на Lua из игры STALKER.
Установка и компиляция Lua.
Скачайте архив с исходным кодом для последней версии с www.lua.org(на данный момент это версия Lua 5.1.2), распакуйте архив и запустите файл luavs.bat для сборки под M$ windows (при этом у Вас должны быть установлены все необходимые для работы VC пути и переменные окружения).
В результате этого (для M$ windows) будут собраны два выполнимых файла lua.exe и luac.exe, а также библиотеки lua51.dll и lua51.lib.
Программа lua.exe позволяет Вам попробовать язык «вживую», без интеграции в какое-либо приложение. Вы просто набираете конструкции на Lua и они сразу же выполняются.
Эта программа крайне удобна как на этапе освоения языка, так и для проверки модулей расширения для языка (написанных на С/С++).
Рис 1. Скриншот программы lua.exe.
Для Linux просто используйте команду make.
Программа luac.exe служит для компиляции исходных программ на Lua в байт-код, который сразу же может быть выполнен (в случае когда необходимо постоянно загружать и выполнять одни и те же программы на Lua за счет их предварительной компиляции в байт-код можно получить заметный выигрыш в скорости загрузки.
Библиотека lua51.dll нужна для работы приложения, использующего скрипты на Lua, а lua51.lib служит для линковки библиотеки в приложение.
Язык Lua.
В качестве идентификаторов в языке Lua могут использоваться любые последовательности из букв, цифр и символа подчеркивания (‘_’), начинающиеся не с цифры. Язык различает регистр букв, поэтому abc, Abc, ABC являются различными идентификаторами.
Следующие слова зарезервированы в Lua и используются в качестве ключевых слов:
Кроме того, все имена, начинающиеся с символа подчеркивания, за которым идут заглавные буквы (например, _VERSION) также считаются зарезервированными.
Lua поддерживает следующие типы данных — nil, boolean, number, string, function, userdata, thread и table.
Тип nil является специальным типом — это тип значения (константы) nil. Основным свойством этого значения является его отличие от любых других значений.
Тип number служит для представления вещественных (double) значений.
Числовые значения в Lua практически полностью идентичны числовым значениям в С, например — 3, 3.0, 3.1415926, 314.16e-2, 0xff).
Тип string соответствует строкам. Строка в Lua рассматривается как последовательность 8-битовых символов (байт). Строки могут содержать любые байты, в том числе и ‘\0’.
Строковые значения это просто последовательности символов, заключенные в обычные или двойные кавычки. При этом внутри строки можно задавать специальные (escape) символы, аналогично языку С — ‘\b’, ‘\n’, ‘\r’, ‘\t’, ‘\\’, ‘\», ‘\»‘, ‘\0’ и ‘\ddd’ (в последнем случае в через ddd обозначен десятичный код символа от нуля до 255).
Также для задания строк можно использовать так называемые длинные скобки (long brackets). Открывающая длинная скобка уровня n состоит из обычной открывающей квадратной скобки (‘[‘), n знаков равенства (‘=’) и еще одной открывающей квадратной скобки (‘[‘). Так открывающая скобка нулевого уровня это просто [[, открывающая скобка первого уровня это [=[ и т.д.
Полностью аналогично вводится закрывающая скобка уровня n.
Длинная строка (точнее строковое значение) — это последовательности символов, следующая за открывающей длинной скобкой уровня n и до закрывающей длинной скобкой того же уровня.
При этом такие строки могут могут занимать несколько строк в файле, в них не поддерживаются escape-символы (т.е. символы вида ‘\n’,’\\’, ‘\xxx’) и игнорируются длинные скобки любого другого уровня.
Принято, что если сразу же за открывающей длинной скобкой идет переход на следующую строку, то он не включается в соответствующее строковое значение.
Часть строки, следующая за двумя знаками минус (—) и до конца строки является комментарием. Также можно использовать многострочные (длинные) комментарии — они аналогичны длинным строковым значениям, только начинаются с двух знаков минус, за которыми идет открывающая длинная скобка уровня n и заканчивается длинной закрывающей скобкой того же уровня (это очень напоминает «»» в языке Python).
Булевский тип (boolean) состоит всего из двух значений — false и true. Обратите внимание, что в Lua только nil и false являются ложными значениями (при использовании их в условных конструкциях), а все остальные значения (в том числе и пустые строки) являются истинными.
Тип userdata служит для хранения произвольных данных (структур в С) в переменных Lua. Фактически значения этого типа соответствуют просто блокам памяти и не имеют никаких предопределенных операций, кроме сравнения и присваивания. Однако за счет использования метатаблиц для значений этого типа можно вводить свои операции и методы.
Обратите внимания, что значения этого типа не могут быть непосредственно созданы в программах на Lua, а должны создаваться только через C API (т.е. в вызывающей скрипт программе).
Тип thread соответствует нити выполнения. Однако эти нити никаким образом не связаны с операционной системой и поддерживаются исключительно средствами самого Lua.
Тип function соответствует функциям. Функции в Lua являются полноценными сущностями — они могут быть записаны в переменные, переданы как параметры в другие функции и возвращены как результат выполенения функций.
Тип table соответствует ассоциативным массивам (хэшам), т.е. значения этого типа являются массивами, которые могут индексироваться не только целочисленными значениями, но и любыми другими значениями кроме nil.
Для удобства работы можно вместо индексирования таблицы по имени (строке) использовать это имя как имя поля структуры, т.е. вместо t[«abc»] можно использовать t.abc. Эти две формы обращения полностью эквивалентны.
Lua — язык с динамической типизацией, подобно Python или Ruby — тип связывается не с переменной, а с ее значением в данный момент. При этом любой переменной может быть присвоено значение любого типа (вне зависимости от того значения, которое она содержала раньше). До первого присваивания переменная содержит значение nil.
Любое значение в Lua (в том числе и функция) может быть запомнено в переменной, передано как параметр в функцию или возвращено как результат выполнения функции.
Таблицы, функции, нити и userdata являются объектами. Это значит, что переменная содержит не непосредственное значение, а ссылку на соответствующий объект. Поэтому присваивание, передача как параметра в функцию и возврат как результат выполнения функции происходит по ссылке. Остальные данные являются непосредственными значениями.
Для того, чтобы узнать тип значения служит библиотечная функция type, возвращающая строку, описывающую тип переданного значения.
В Lua при необходимости осуществляется автоматическое преобразование чисел в строки и строк в числа. Так если строковое значение является операндом в арифметической операции, то оно преобразуется в число. Аналогично числовое значения, встретившееся в том месте, где ожидается строковое, будет также преобразовано в строку.
Переменные в Lua служат для хранения значений и могут быть трех типов — глобальные, локальные и поля таблиц. Простой идентификатор всегда соответствует глобальной или локальной переменной.
По умолчанию, все переменные в Lua являются глобальными, локальные переменные должны быть явно объявлены как локальные. До первого присвоения любая переменная содержит значение nil.
Все глобальные переменные являются полями в специальных таблицах Lua, называемых таблицами окружения (environment tables). Каждая функция содержит свою ссылку на соответствующую таблицу окружения. При создании новой функции она наследует таблицу окружения.
Операторы Lua
Lua поддерживает достаточно большой набор операторов, аналогичных большинству распространенных языков программирования. Для разделения операторов служит точка с запятой (‘;’). Однако в Lua нет пустого оператора, поэтому две подряд идущие точки с запятой (‘;;’) недопустимы. Сама точка с запятой не является частью оператора и используется только для разделение нескольких операторов, расположенных на одной строке.
Группа операторов может быть объединена в блок (составной оператор) при помощи конструкции do-end.
1. Оператор присваивания
В простейшей форме оператор присваивания в Lua выглядит следующим образом:
Кроме того Lua (подобно Python и Ruby) поддерживает множественное присваивание, когда одним оператором присваивания может быть присвоено сразу много значений — в этом случае слева от знака равенства (‘=’) стоит список переменных, а справа — список выражений.
Так для того чтобы поменять местами значения переменных x и y можно воспользоваться следующим оператором:
Перед выполнением присваивания, сперва вычисляются значения всех участвующих выражений, после чего осуществляется приведение длины списка значений к длине списка переменных. Лишние значения просто отбрасываются, если значений не хватает, то список значений дополняется необходимым количеством nil‘ов.
2. Оператор if
Условный оператор в Lua имеет следующий вид:
Через квадратные скобки обозначена необязательная честь, через фигурные скобки — часть, которая может быть повторена много раз.
Как уже отмечалось, любое значение выражения, кроме nil и false считается истинным.
3. Оператор while
Оператор цикла while имеет следующий вид:
Для преждевременного выхода из цикла могут использоваться операторы break и return, но эти операторы могут быть только последними операторами блока, поэтому в остальных случаях может понадобиться заключить эти операторы в свой блок do-end.
4. Оператор repeat
Данный оператор имеет следующий вид:
Если внутри блока вводится локальная переменная, то она может быть использована в условии окончания цикла:
5. Простая форма оператора for
Оператор цикла for может быть записан в одной из двух форм. Простейшая из них имеет следующий вид:
Это традиционная форма оператора цикла с заданием начального и конечного значения, также можно задать величину шага. Если шаг не задан, то он считается равным единице. Обратите внимание, что все два (или три) выражения вычисляются всего один раз, перед началом цикла.
Для преждевременного выхода из цикла также как и для всех остальных операторов цикла (while, repeat) можно использовать операторы break и return, но они должны быть последними операторами в непосредственно содержащем их блоке.
Сама переменная цикла является локальной для оператора цикла и по его окончании не определена.
6. Оператор for с использованием итераторов
Это наиболее общий вид оператора цикла for, каждый раз использующий вызов функции для определения нового значения переменных цикла. Остановка цикла происходит при получении значении nil.
Эта форма оператора имеет следующий вид:
Рассмотрим оператор следующего вида:
Он будет эквивалентен следующей группе операторов:
Обратите внимание, что explist выполняется всего один раз и возвращает функцию-итератор (f), состояние (s) и начальное значение для первого параметра цикла.
Для итерирования по таблицам существует специальные функции ipairs и pairs:
Различие между этими функциями заключается в том, что первая из них служит для итерирования по таблице с целочисленными индексами, а вторая — для общего случая, когда в качестве индекса может выступать произвольный объект (кроме nil).
7. Вызов функции как оператор
Lua позволяет использовать вызовы функций как операторы. В этом случае все возвращаемые функцией значения просто отбрасываются.
8. Объявления локальных переменных
Все локальные переменные в Lua должны быть явно объявлены (в противном случае, соответствующая переменная будет считаться глобальной).
Для этого служит оператор local, который может располагаться в любом месте блока. Объявления локальной переменной может также включать в себя присвоение начального значения (у всех переменных, начальное значение которых не было задано, в качестве начального значения выступает nil).
Локальными могут быть и функции:
Выражения в Lua
Lua поддерживает все стандартные арифметические операции (+,-,*,/), включая остаток от деления (%) и возведение в степень (^).
Арифметические операторы применимы и к числам и к строкам, которые в этом случае преобразуются в числа (поэтому оператор + нельзя использовать для конкатенации строк — это арифметический оператор).
Также в Lua определены следующие операции сравнении величин:
Эти операторы всегда возвращают булевское значение (т.е. true или false). Обратите внимание, что оператор «не равно» в Lua это ‘
=’ (в отличии от C/С++).
Правила преобразования чисел в строки и наоборот при сравнениях не работают, т.е. выражение «0»==0 дает в результате false.
Также в Lua определены логические операции and, or и not. Оператор not всегда возвращает true или false.
Оператор and возвращает свой первый операнд, если он false или nil. В противном случае он возвращает второй операнд (при этом этот операнд может быть произвольного типа).
Оператор or возвращает первый операнд, если он не false и не nil, иначе он возвращает второй операнд.
Таким образом, логические операторы and и or могут возвращать значения любых типов. При этом эти операторы вычисляют значение второго операнда только, если его нужно вернуть. В противном случае вычисление второго операнда не происходит. Так в выражении 10 or foo () вызова функции foo не произойдет.
Поскольку оператор ‘+’ является арифметическим, то его нельзя использовать для конкатенации строк — для этих целей служит оператор ‘..’. При этом если один или оба операнда окажутся числами, то произойдет преобразование числовых значений в строки.
Также в Lua определен оператор длины ‘#’. Длина строки — это количество байт в этой строке. Длина таблицы t определяется как такое целое значение n, что t[n]
= nil, а t[n+1]==nil.
Если t — обычный массив с элементами а в позициях 1, 2, . n, то его длина будет равна n.
Конструктор таблицы также является выражением. Он используется для создания таблиц и представляет собой список полей в фигурных скобках. Поля отделяются друг от друга запятыми (‘,’) или точками с запятой (‘;’). При этом допускается наличие разделителя после последнего поля.
Просто пара фигурных скобок ‘<>‘ создает пустую таблицу. Чтобы получить обычный массив (в Lua принято индексировать массивы начиная с единицы) просто задаются значения элементов.
Также отдельное поле может иметь вид name=expr. В этом случае задаются именованные поля, т.е. поля, ключом к которым служат имена полей, позволяя тем самым создавать аналоги структур.
Возможен также случай, когда задание поля имеет вид [expr]=expr, например t= . Этот пример эквивалентен следующим операторам:
В отличии от традиционных языков программирования в Lua можно смешивать все виды задания полей. Однако, пока это возможно Lua старается хранить массивы именно как массивы, а не хэши, что делает работу с массивами быстрее.
Вызов функции также является выражением и может принимать один из следующих видов:
Последняя форма служит аналогом вызова методов — запись v:name(args) эквивалента обычному вызову с дополнительным параметром v.name(v,args) (обратите внимание, что вычисление значения v происходит всего один раз).
Значения всех аргументов функции вычисляются до момента вызова функции. Кроме того вызов вида f(fields) переводится в f(
Аналогично вызов f’string’ (или f»string: или f([string])) переводятся в f(«string») .
Обратите внимание, что в Lua нельзя ставить перевод строки перед открывающей круглой скобкой ‘(‘, открывающей список аргументов, так как это создает неоднозначность — ведь в Lua функция это объект, который можно присваивать переменным.
В ряде случаев необходимо получить лишь первое значение и массива значений, возвращаемых выражением. В этом случае достаточно заключить это выражение в круглые скобки — это как раз дает первое значение из получаемого массива.
Приоритеты операций в Lua
Приоритеты операций в Lua определены следующим образом:
Определение функций в Lua
Формальный синтаксис для определения функции функций в Lua выглядит следующим образом:
funcbody определяется одним из следующих видов:
Этот способ задает анонимную функцию, т.е. функцию без имени — ее можно либо сразу же использовать в выражении, либо передать как параметр, либо вернуть как результат выполнения другой функции. Параметры функции являются локальными переменными, определенными внутри данной функции.
Приведенный выше пример возвращает функцию, возводящую свой аргумент в квадрат.
Задание именованной функции выглядит следующим образом:
Последний случай задает именованную локальную функцию — она будет доступна только в пределах того блока, в котором она объявлена.
Имя функции funcname выглядит следующим образом:
Здесь через фигурные скобки обозначена часть, которая может повторяться ноль и более раз, а через квадратные скобки — необязательная часть.
Приведенный выше пример на самом деле эквивалентен следующей конструкции —
Т.е. объявление именованной функции фактически создает анонимную функцию и присваивает ее соответствующей переменной или полю таблицы. Поэтому следующий пример
на самом деле эквивалентен приведенному ниже фрагменту кода.
Ниже приводится пример функции, вычисляющей значение факториала.
Также можно (как и в С/С++) закончить список параметров многоточием (‘. ‘). Это означает, что у функции могут быть дополнительные параметры, кроме явно указанных (до многоточия). При этом при вызове такой функции не происходит отсечение списка передаваемых в функцию параметров — этот список передается целиком.
Для доступа к параметрам, соответствующим многоточию в списке параметров, можно использовать специальную переменную arg
Функция может возвращать сразу много значений, и их не надо для этого «упаковывать» в таблицу — просто в операторе return укажите список возвращаемых значений через запятую:
Если имеется таблица, которую мы хотим вернуть не как одно значение (таблица), а именно как список значений, то для этой цели можно использовать функцию unpack:
Так если вызвать первую функцию, то она вернет все переданные аргументы как таблицу, т.е. один объект. Вторая функция вернет просто все переданные аргументы как отдельные значения, которые могут быть использованы во множественном присваивании.
Можно создав анонимную функцию, сразу же вызвать ее, нигде не сохраняя ее:
Язык Lua обладает очень простыми правилами видимости — область видимости переменной (функции) начинается с первого оператора, после ее объявления, и заканчивается концом самого внутреннего блока, содержащего ее объявление.
Обратите внимание, что область видимости начинается оператора, после объявления этой переменной, т.е. в самом операторе объявления она еще не видна. Поэтому в следующем примере происходит инициализация локальной переменной x значением глобальной (или внешней) переменной x.
Еще одной интересной особенностью Lua является то, что каждое выполнение оператора local создает новую локальную переменную. Рассмотрим следующий пример:
В результате выполнения этого фрагмента кода будет создано 10 анонимных функций, каждая со своим экземпляром локальной переменной y, но с общей локальной переменной x.
Все переменные, на которые ссылается локальная функция, связываются с ней и сохраняют свои значения. Таким образом функции держит свои «личные» копии локальных переменных. Так в рассмотренном выше примере каждая из десяти созданных функций ссылается на свою копию локальной переменной y и на общую копию локальной переменной x.
Модули в Lua
Lua поддерживает модули, т.е. возможность вынесения некоторого кода (переменных и функций) в отдельные модули (файлы), которые потом можно подгружать и обращаться к содержащимся в них переменным и функциям.
Ниже приводится простой пример такого модуля — пусть файл a.lua содержит приведенный ниже код.
Тем самым мы создали модуль «a», к которому можно обратиться из других файлов
Для работы с модулями Lua использует специальную таблицу package, в которой хранится информация о загруженных модулях. В частности этим можно воспользоваться для того, чтобы перезагрузить модуль (в случае если он был изменен с момента загрузки):
При задании модуля как показано выше, все определенные в нем величины (не являющиеся локальными) будут доступны из других модулей. Поэтому если необходимо «спрятать» что-то (сделать недоступным извне данного модуля), то соответствующие переменные и функции описываются как локальные.
В полях package.path и package.cpath содержатся разделенные точкой с запятой списки шаблонов для загрузки модулей Lua и бинарных модулей. За счет изменения этих полей можно обеспечить поиск загружаемых модулей в нужных каталогах:
Метатаблицы и метаметоды
Вроде бы явно процедурный язык Lua обладает очень мощным механизмом расширения, позволяющим фактически ввести полноценные объекты и легко изменять поведение стандартных типов, таких как table и userdata.
С каждым значением в Lua можно связать так называемую метатаблицу — таблицу функций, которые должны вызываться вместо стандартных операторов, таких как оператор сложения, и некоторых специальных операций, таких как доступ к полям таблицы.
Ключи в этой таблице называются событиями (events) и являются строками (именами событий). Значения, соответствующие ключам, являются обычно функциями, которые должны вызываться при наступлении соответствующих событий. В следующей таблице приводятся поддерживаемые события.
Для работы с метатаблицей служат функции setmetatable и getmetatable.
| Событие | Оператор | Комментарий |
|---|---|---|
| __add(op1, op2) | + | бинарный плюс — сложение объектов |
| __sub(op1, op2) | — | бинарный минус — вычитание |
| __mul(op1, op2 | * | умножение |
| __div(op1, op2) | / | деление |
| __mod(op1, op2) | % | остаток от деления |
| __pow(op1, op2) | ^ | возведение в степень |
| __unm(op) | — | унарный минус |
| __concat(op1, op2) | .. | конкатенация строк |
| __len(op) | # | длина |
| __eq(op1, op2) | == | оператор «равно» |
| __lt(op1, op2) | < | оператор «меньше» |
| __le(op1, op2) | <= | оператор «меньше или равно» |
| __index(op, key) | [] | чтения по ключу |
| __newindex(op, key) | [] | запись по ключу |
| __call(op, . ) | () | вызов как функции |
| __gc | вызывается для userdata-объектов при сборке мусора | |
| __metatable | Это поле позволяет защищать метатаблицы объектов — при установленном этом поле блокируется доступ к метатаблице объекта. Если задать это поле в метатаблице, то getmetatable будет просто возвращать его значение. setmetatable вернет ошибку. | |
| __tostring | Функция, вызываемая tostring для перевода объектов в строковое представление. Значением поля является функция, получающая на вход объект и возвращающая его строковое представление |
Большая группа событий связана со стандартными операторами — сложение, вычитание, умножение, деление, конкатенация и т.п. За счет задания обработчиков соответствующих событий можно легко добавить таблице с полями x и y возможность сложения, умножения и т.п., т.е. превратить в полноценный двухмерный вектор.
Обратите внимание, что нам было достаточно определить метатаблицу с операцией сложения всего для одного из операндов — Lua сначала ищет метатаблицу и обработчик события у первого операнда, а затем (в случае неудачи) — у второго.
Задать функцию __add можно и альтернативным способом
Однако определенная выше операция не совсем корректна — она возвращает таблицу с правильными значениями полей x и y, но для этой таблицы операция сложения уже не определена. Это можно легко исправить, если в возвращаемой таблице установить метатаблицу, как показано ниже.
Подобным образом можно легко определить для таблицы с полями x и y все основные арифметические операции, превращающие такую таблицу в полноценный двухмерный вектор.
Обратите внимание, что если для всех арифметических операторов есть соответствующие события (включая и унарный минус), то для операторов сравнения событий крайне мало — всего __eq, __lt и __le. Все остальные операторы сравнения выводятся из них через отрицание, так оператор
= определяется как not ==.
Более интересными событиями являются __index и __newindex.
Событие __index возникает при каждом обращении на чтение к элементу таблицы. Задав поле __index в метатаблице мы можем изменить механизм доступа к элементам таблицы (объекта). Простейший способ достижения этого — явное задание функции обработки данного сообщения:
В приведенном выше примере мы воспользовались стандартной функцией rawget обеспечивающей прямой доступ к таблице, минующий событие __index. При каждом обращении к таблице будет печататься ключ доступа, после чего возвращается корректное значение из таблицы.
Так можно легко добавить двухмерному вектору поле length, которое будет на самом деле просто вычислять длину вектора и возвращать ее:
В качестве обработчика события __index можно задать не функцию, а другую таблицу. Тогда если в исходной таблице запрашиваемого поля нет, то поиск продолжится в указанной таблице. Если и в ней поле не будет найден, но у нее есть метатаблица с полем __index , показывающим на еще одну таблицу, то поиск продолжится в ней и т.д.
Событие __newindex происходит каждый раз при записи значения в таблицу. Так за счет задания обработчика данного события можно легко защитить отдельное поле от изменения:
Событие __call позволяет вызывать объект, как функцию (т.е. фактически определяет оператор ()).
Событие __gc для userdata-объектов позволяет задать поведение при уничтожении объекта сборщиком мусора — все такие объекта сперва собираются в список, потом к каждому из них посылается событие __gc (передавая в качестве параметра сам уничтожаемый объект), только после чего объект уничтожается. События посылаются в порядке, обратном созданию объектов.
Стандартные функции в Lua
Все стандартные функции в Lua можно разбить на следующие несколько групп — основные (basic), работа с модулями (package), работа со строками, работа с таблицами, математические функции, ввод/вывод, доступ к ОС, средства отладки. Для ряда групп соответствующие функции собраны в таблицы, соответствующие группам, например string, math и т.п.
Ниже приводятся основные функции для этих групп.
Таблица 1. Основные функции.
«stop»: остановка сборки мусора.
«restart»: снова запустить сборщик мусора.
«collect»: осуществить полный цикл сборки мусора.
«count»: возвращает полный объем используемой Lua памяти в Кбайтах.
«step»: выполняет один шаг сборки мусора. Параметр size управляет величиной шага.
«setpause»: устанавливает arg/100 в качестве нового значения для остановки сборщика.
Таблица 2. Работа с модулями.
Если есть глобальная таблица с именем name, то она становится модулем. В противном случае создается новая таблица с именем name, она заносится в package.loaded[name].
В этой таблице в поле _NAME записывается имя модуля, в поле _M записывается ссылка на себя, а в поле _PACKAGE — имя пакета.
Все функции для работы со строками находятся в таблице string. Индексирование символов начинается с единицы. Допустимы отрицательные индексы (-1 указывает на последний символ, -2 — на предпоследний и т.д.).
Таблица 3. Работа со строками.
Так следующий пример напечатает все слова из строки
Следующий пример разбирает строку, содержащую набор присваиваний вида key=value и заносит их в таблицу.
Обратите внимание на использование события __index в метатаблице — обработчик этого события ссылается на таблицу самого класса (таблицу методов). За счет этого каждый раз, когда идет обращение к методу объекта или класса будет происходить обращение к таблице класса (поскольку имен методов в метатаблице нет).
Довольно легко реализовать наследование за счет «сцепление» таблиц классов при помощи события __index:
C API для Lua
Все необходимые типы, функции и константы определены в файле lua.h, кроме этого также необходима библиотека lua51.lib.
Подобно OpenGL и другим библиотекам все функции в Lua начинаются с префикса lua_, а константы — с префикса LUA_.
Для работы с Lua необходимо сначала создать контекст для работы (иногда вместо термина контекст используется термин виртуальная машина). У вас может быть несколько независимых контекстов в каждом из которых происходит работа. Все остальные функции C API получают этот контекст как первый параметр. Для создания контекста служат следующие функции:
Первая из этих функция на самом деле сейчас определена как макрос, вызывающий luaL_newstate и является устаревшей функцией. Функция lua_newstate создает новое состояния и при этом получает в качестве параметров функцию выделения памяти и параметр, передаваемый этой функции при ее вызове. Функция luaL_newstate использует стандартную функцию выделения памяти, основанную на функции realloc и служит заменой lua_open.
При ошибке создания контекста возвращается значение NULL.
Тип lua_Alloc определен следующим образом:
Для освобождения контекста служит функция lua_close:
Одной из особенностей Lua является то, что передача значений между программой на С/С++ и Lua происходит через специальный стек, каждый элемент этого стека является значением в Lua.
При вызове функции С/С++ из Lua вызываемая функция получает свой стек, независимый от предыдущих стеков и стеков других функций, активных на данный момент.
Изначально стек содержит все аргументы для функции в прямом порядке (т.е. первый аргумент помещается на стек первым), на этот же стек функция заносит возвращаемые значения.
При этом используемый стек (в отличии от традиционного понимания стека) позволяет доступ (как на чтение, так и на запись) к каждому своему элементу при помощи целочисленного индекса. Положительный индекс соответствует абсолютному положению (индексы начинается с единицы), отрицательные значения соответствуют смещению с вершины стека к его началу.
Так если стек содержит n элементов, то индекс 1 соответствует первому элементу (первому аргументу вызываемой функции), индекс 2 — второму, индекс n соответствует последнему (верхушке стека). Индекс -1 соответствует последнему элементу, -2 — предпоследнему элементу, индекс -n — первому элементу (см. рис). Индекс i является валидным (т.е. может использоваться для обращения к элементам стека) если 1<=abs(i)<=n. Нулевое значение не является валидным индексом.
Рис 2. Индексация в стеке.
Вы сами должны отвечать за то, чтобы не произошло переполнение стека (размер стека фиксирован, по умолчанию он равен 20). Для явного задания размера стека служит функция lua_checkstack:
После этого вызова размер стека будет составлять не менее extra элементов. При ошибке (не удалось увеличить размер стека) возвращается ноль. Обратите внимание, что данная функция может только увеличить размер стека, но не уменьшить.
Кроме стандартных индексов для обращения в стек можно использовать и так называемые псевдо-индексы. Псевдо-индексы соответствуют доступным значениям Lua, не находящимся на стеке.
Доступ к глобальным переменным осуществляется через псевдоиндекс LUA_GLOBALSINDEX. Текущее окружение доступно через псевдоиндекс LUA_ENVIRONINDEX.
Для доступа к глобальной переменной используется обычный механизм доступа к полям таблицы. Так для доступа к глобальной переменной x используется следующий вызов:
Этот вызов возьмет значение глобальной переменной x и добавит на вершину стека.
Как и в «маленьких мягких окошках» в Lua есть свой реестр. Только здесь это просто специальная таблица, в которой код из С/С++ может хранить значения. Для доступа к этой таблице служит псевдоиндекс LUA_REGISTRYINDEX. Не рекомендуется осуществлять запись в таблицу реестра по целочисленным ключам — они используются Lua.
Далее мы рассмотрим основные (наиболее часто используемые) функции для работы с Lua, а потом перейдем к практическим примерам использования Lua в программах на С/С++.
Эта функция служит для вызова функции. Перед ее вызовом следует сперва поместить вызываемую функцию на стек, а затем все передаваемые аргументы в прямом порядке (первый аргумент первым, затем второй и т.д.).
В параметре numArgs передается количество переданных аргументов. После вызова все переданные аргументы автоматически убираются со стека, а вместо них в стек заносятся результаты выполнения функции в естественном порядке.
Если значение параметра numResults не равно LUA_MULTRET, то на стеке будет оставлено ровно numResults значений. Лишние значения убираются со стека, вместо недостающих заносятся nil.
Функция lua_pcall отличается тем, что в случае ошибки просто помещает на стек сообщение об ошибке и возвращает код ошибки. Код ошибки может быть равен нулю (все в порядке) или же принимать одно из следующих значений — LUA_ERRRUN (ошибка во время выполнения), LUA_ERRMEM (ошибка выделения памяти) и LUA_ERRERR (ошибка во время выполнения обработчика ошибок).
Если errFunc равно нулю, то возвращается стандартное сообщение об ошибке. В противном случае errFunc — это индекс функции для обработки ошибок (в данной реализации это не может быть псевдоиндекс).
Данная функция берет значение из стека по заданному индексу, извлекает из него поле с заданным именем и значение этого поля помещается на вершину стека.
Пусть в стеке была таблица t. Тогда следующие рисунки иллюстрируют результаты вызовов функции lua_getfield.
Рис 3. Работа функции lua_getfield.
Данная функция в таблице, находящуюся в стеке по заданному индексу устанавливает значение поля с заданным именем в значение с вершины стека. При этом значение снимается с вершины стека.
Рис 4. Работа функции lua_setfield
Данный вызов удаляет с вершины стека n значений.
Функция добавляет на вершину стека логическое значение value (0 или 1).
Функция добавляет на вершину стека число value.
Функция добавляет на вершину стека копию строки с длиной len (маркер ‘\0’ игнорируется).
Функция помещает на вершину стека значение nil.
Функция добавляет на вершину стека число value.
Функция помещает на вершину стека строку, законченную символом ‘\0’.
Функция помещает копию значения, расположенного по заданному индексу на вершину стека.
Аналогично функции sprintf помещает на вершину стека результат форматированного вывода в строку. Функция сама отслеживает необходимый объем памяти под строку. Однако есть ограничения на использование спецификаций форматов — не поддерживается точность и флаги.
Удаляет элемент с заданным индексом из стека, сдвигая выше расположенные элементы.
Рис 5. Работы функции lua_remove.
Возвращает индекс элемента на вершине стека, т.е. количество элементов в стеке.
Устанавливает новое количество элементов в стеке. При этом лишние элементы автоматически удаляются, при необходимости стек дополняется nil-ми.
Перемещает элемент с вершины стека по заданному индексу сдвигая элементы.
Рис. 6. Работа функции lua_insert.
Помещает на вершину стека значение t[k], где t — таблица, расположенная по заданному индексу, а ключ k — элемент с вершины стека.
Создает новую пустую таблицу и помещает ее на вершину стека. При этом в таблице сразу резервируется место для narr элементов массива и nass для элементов хэша (обычно только один из этих параметров отличен от нуля).
Создает новую таблицу, эквивалентен lua_createtable ( lua, 0, 0 ).
Функция выделяет блок памяти заданного размера и создает соответствующий объект типа userdata, помещая его на стек. Возвращается указатель на выделенный блок памяти.
Возвращает единицу, если элементы по заданным индексам равны и ноль в противном случае.
Помещает на вершину стека метатаблицу объекта, расположенного в стеке по заданному индексу. Если индекс не является валидным или у соответствующего объекта нет метатаблицы, то возвращается ноль и на стек ничего не кладется.
Снимает таблицу с вершины стека и устанавливает ее в качестве метатаблицы для объекта по заданному индексу
Возвращает единицу, если значение по заданному индексу является логическим (типа boolean) и ноль в противном случае.
Возвращает единицу, если значение по заданному индексу является функцией (типа funtion) и ноль в противном случае.
Возвращает единицу, если значение по заданному индексу является nil и ноль в противном случае.
Возвращает единицу, если переданный индекс является недопустимым.
Возвращает единицу, если либо переданный индекс является недопустимым, либо по этому индексу расположен nil.
Возвращает единицу, если значение по заданному индексу является числом (типа number) и ноль в противном случае.
Возвращает единицу, если значение по заданному индексу является строкой (типа string) и ноль в противном случае.
Возвращает единицу, если значение по заданному индексу является таблицей (типа table) и ноль в противном случае.
Возвращает единицу, если значение по заданному индексу является типа userdata и ноль в противном случае.
Возвращает единицу, если значение, расположенное по заданному индексу истинно и ноль в противном случае.
Преобразует значение по заданному индексу в знаковое целое число и возвращает его. Соответствующее значение должно быть числом или строкой.
Преобразует значение со стека по заданному индексу в строку, если length не равен NULL, то в него заносится длина строки. Возвращается указатель на завершенную ‘\0’ строку, расположенную во внутреннем буфере Lua. Соответствующее значение должно быть строкой или числом.
Преобразует значение по заданному индексу в число (обычно это double) и возвращает его. Значение должно быть строкой или числом.
Эквивалентно lua_tolstring ( lua, index, NULL ).
Если значение по заданному индексу имеет тип userdata, то возвращает указатель на соответствующий блок памяти. Иначе возвращает NULL.
Возвращает тип объекта по заданному индексу или LUA_TNONE в случае недопустимого индекса. Тип объекта принимает одно из следующих значений — LUA_TNIL, LUA_TNUMBER, LUA_TBOOLEAN, LUA_TSTRING, LUA_TTABLE, LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD и LUA_TLIGHTUSERDATA.
Переводит константы типов в соответствующие строковые значения.
Создает новую глобальную функцию с именем name, на основе С-функции и делает ее доступной программам на Lua.
Тип lua_CFunction определен следующим образом:
Фактически каждая функция на С/С++, которую мы хотим экспортировать должна иметь такой вид — все аргументы она получает на стеке, результаты также помещает на стек и возвращает количество результатов, записанных на стек.
Полностью аналогично lua_gettable, но для обращения к таблице не использует метаметоды.
Если через t обозначить таблицу по индексу index в стеке, то данный вызов возвращает значение (без использования метаметодов) t[n].
Полностью аналогично lua_settable, но для записи в таблицу не использует метаметоды.
Если через t обозначить таблицу по индексу index в стеке, а через v значение на вершине стека, то данный вызов делает прямую запись (без использования метаметодов) t[n]=v.
Пусть у нас есть следующий фрагмент кода на Lua:
Он будет эквивалентен следующему фрагменту кода на С/С++.
Рис. 7. Стек во время вызова.
Кроме перечисленных функция в C API также в входит дополнительная библиотека. Заголовочным файлом для нее служит файл lauxlib.h, а имена всех функций начинаются с префикса luaL_.
Все функции дополнительной библиотеки построены на основе основного API, но в ряде случаев более удобны.
Открывает и делает доступными в текущем контексте для скриптов на Lua все стандартные библиотеки.
Если объект по заданному индексу содержит метатаблицу и в ней есть поле с именем eventName, то это поле вызывается (как функция) и ей в качестве параметра передается исходный объект (с вершины стека).
Загружает файл с именем fileName и помещает его на стек как функцию (поэтому после загрузки необходимо сделать вызов lua_call или lua_pcall).
При успехе возвращает 0, иначе один из следующих кодов — LUA_ERRSYNTAX, LUA_ERRMEM или LUA_ERRFILE.
Загружает код на Lua из строки и помещает его на стек как функцию.
Загружает и выполняет заданный файл. Определен как следующий макрос:
Открывает библиотеку с заданным именем libName. Если libName равно NULL, то просто регистрирует все функции из массива reg (концом массива служит запись с обоими значениями равными NULL).
Если libName не равно NULL, то создается новая таблица как глобальная переменная и именем модуля и в ней регистрируются все функции из массива reg.
Примеры интеграции скриптов на Lua в программы на C/C++
Первый наш пример просто загружает скрипт и выполняет его. Ниже приводится соответствующий скрипт, его задача простейшая — просто вывести сообщение.
Ниже приводится код на С++. Его задача создать контекст, открыть стандартные библиотеки и выполнить заданный файл.
Все это и делает приведенный ниже исходный код.
Следующий пример будет несколько сложнее — нашей задачей будет вызвать определенную в скрипте функцию и получить от нее результат.
Пример функции приведен ниже, он просто печатает входные параметры и возвращает их суммы, хотя это может быть любая требуемая функциональность.
Соответствующий код на С++ должен не просто создать констекст и загрузить библиотеки. Также необходимо загрузить файл с нашей функций и вызвать lua_pcall, чтобы вызвать этот файл и определенная в нем функция стала доступна.
После этого мы просто помещаем вызываемую функцию и необходимые параметры на стек и делаем вызов lua_pcall. После него на стеке останется результат выполнения функции.
Наш следующий пример сделает обратное — определит функцию на С++ и проэкспортирует ее в Lua. После этого будет выполнен скрипт, который вызовет нашу функцию.
В качестве примера экспортируемой функции возьмем нахождение среднего из переданных аргументов. При этом пусть возвращается как само среднее значение, так и число переданных аргументов. Ниже приводится скрипт, демонстрирующий использование данной функции.
Нам необходимо создать функцию и экспортировать ее в Lua. После этого можно звать сам скрипт. Ниже приводится соответствующий исходный код, обратите внимание, на то что функция возвращает 2, т.к. ровно два выходных значения положены на стек.
Это первая часть, посвященная Lua. Во второй части будет рассмотрена работа с программой toLua, позволяющей автоматически генерировать интерфейсы с С/С++ для Lua, а также будет рассмотрен JIT-компилятор для Lua — LuaJIT.