Я хочу убедиться, что я понимаю, что вы делаете, прежде чем мы увидим, где вы, кажется, идете не так.У вас есть файл сценария Lua.Вы хотите выполнить этот скрипт, передав ему один строковый аргумент.Он будет выполнять некоторые вещи, а затем возвращает ноль или более строк в качестве возвращаемых значений.И вы хотите получить эти значения в своем коде.
ОК, давайте начнем с вершины:
if ((error = lua_pcall(L, 1, LUA_MULTRET, 0)) == 0)
Обычно, когда вы выполняете lua_pcall
, третий параметр сообщает Lua точно сколько возвращаемых значений ожидается.Если вызываемая функция возвращает больше этого числа, эти возвращаемые значения отбрасываются.Если он возвращает меньше этого числа, то для заполнения счетчика используются дополнительные значения NIL.
LUA_MULTRET говорит Lua не делать этого.Когда это используется, все результаты помещаются в стек.
Теперь, так как вы забыли опубликовать свой сценарий, я должен сделать некоторые предположения относительно того, как выглядит ваш сценарий.Вы возвращаете несколько строк, но никогда не говорите, как это происходит.Lua, как язык, допускает множественные возвращаемые значения:
return "string1", "string2";
В результате в стек помещаются 2 строки.Это отличается от:
return {"string1", "string2"};
Это помещает один объект в стек: таблицу.Таблица содержит 2 строки.Видите разницу?
Глядя на ваш код, кажется, что вы ожидаете, что скрипт Lua вернет таблицу строк, а не несколько возвращаемых значений.
В этом случае, вы должны вызвать свой скрипт Lua следующим образом:
if ((error = lua_pcall(L, 1, 1, 0)) == 0)
Это говорит Lua, что вы ожидаете одно возвращаемое значение, и если пользователь не предоставит его, Lua вставит NIL в стек.
Теперь поговорим о стеке.Состояние стека было таким до вызова функции:
2- {string: path.c_str()}
1- {function: loaded from file "src/test.lua"}
Это от вершины стека до «низа».Если вы используете lua_pcall
, который я вам дал, вы получите в стеке следующее:
1- {return value}
lua_pcall
удалит аргумент (ы) и функцию изстек.Таким образом, он извлечет из стека N + 1 элементов, где N - количество аргументов функции Lua, как указано lua_pcall
(второй параметр).Поэтому Lua вытолкнет 2 вещи из стека.Затем он поместит ровно 1 значение в стек: возвращаемое значение (или NIL, если не было возвращаемого значения).
Так что мы можем пройти вызов функции.Если все прошло хорошо, мы теперь ожидаем, что стек будет содержать:
1- {table: returned from function}
Однако, возможно, не все пошло хорошо.Возможно, скрипт вернул NIL.Или что-то другое;нет никаких гарантий, что это был стол.Итак, следующий шаг - проверить возвращаемое значение (примечание: это когда ваш код перестает иметь смысл, так что это все новое).
if(lua_istable(L, -1))
lua_istable
делает именно то, что предлагает имя: определитьесли данный элемент является таблицей.Но что означает это «-1», и почему это не «1», который вы имели в своем коде?
Этот аргумент является ссылкой на местоположение в стеке.Стек Lua также является регистровым файлом Lua.Это означает, что, в отличие от реального стека, вам разрешается пик на любом элементе в стеке.Элементы в стеке имеют абсолютное местоположение в стеке.Теперь вот как выглядит наш стек снова:
1- {return value}
То, что я написал "1", является абсолютным положением в стеке этого значения.Я могу выдвинуть значения и всплывающие значения, но если я не вытолкну это значение, его местоположение будет всегда равным "1".
Однако это всего лишь "1", потому что наш стек начал пусто .Это несколько грубо, если предположить это (поскольку это может действительно укусить вас, если стек не пуст. Документы Lua услужливо заявляют, когда вы можете предположить, что стек действительно пуст или, если нет, то, что уже находится в стеке).Поэтому вы можете использовать относительные местоположения.
И вот что означает «-1»: это первый индекс стека из top стека.Наша функция lua_pcall
, как определено выше, извлечет 2 элемента из стека (аргумент и функцию) и отправит 1 элемент (возвращаемое значение или NIL).Следовательно, «-1» будет всегда ссылаться на наше возвращаемое значение.
Таким образом, мы проверяем, является ли индекс стека «-1» (вершина стека) таблицей,Если это не так, то не получится.Если это так, то мы можем проанализировать наш список.
И вот здесь мы приступим к анализу списка.Первым шагом является получение количества элементов в списке:
int len = lua_objlen(L, -1);
list_strings.reserve(len);
Второй - просто приятный случай, так что вы не выделяете кучу раз.Вы точно знаете, сколько строк будет в этом списке, поэтому вы также можете заранее сообщить списку, верно?
lua_objlen
возвращает количество элементов массива в таблице.Обратите внимание, что это может вернуть ноль , но наш цикл будет обрабатывать этот случай.
Далее мы идем по столу, вытаскивая строки.
for (int i=0; i < len; i++) {
//Stuff from below.
}
Помните, чтоLua использует 1-базовые индексы.Лично я предпочитаю использовать индексы 0-base в коде C / C ++, даже код, который взаимодействует с Lua.Поэтому я делаю перевод как можно позже.Но вам не нужно.
Теперь, для содержимого цикла.Первый шаг - получить запись таблицы из таблицы.Чтобы сделать это, нам нужно дать Lua индекс и сказать Lua, чтобы он получил этот индекс из таблицы:
lua_pushinteger(L, i + 1);
lua_gettable(L, -2);
Теперь первая функция помещает индекс в стек.После этого наш стек выглядит так:
2- {integer: i + 1}
1- {table: returned from function}
Функция lua_gettable
заслуживает большего пояснения.Он принимает ключ (помните: ключи таблицы в Lua не обязательно должны быть целыми числами) и таблицу и возвращает значение, связанное с этим ключом в этой таблице.Или NIL, если там нет значения.Но то, как это работает, немного странно.
Предполагается, что вершина стека - это ключ.Таким образом, параметр, который он принимает - это расположение в стеке таблицы , в которую будет индексироваться ключ.Мы используем «-2», потому что, ну, посмотрите на стек.Таблица 2 сверху, так как мы выдвинули целое число;поэтому мы используем «-2».
После этого наш стек выглядит следующим образом:
2- {value: from table[i + 1]}
1- {table: returned from function}
Теперь, когда мы получили значение, мы должны убедиться, что это строка, изатем получите его значение.
size_t strLen = 0;
const char *theString = lua_tolstring(L, -1, &strLen);
Эта функция делает все это одновременно.Если значение, которое мы получили из таблицы, не является строкой (или числом, так как Lua автоматически преобразует числа в строки), то theString
будет NULL.В противном случае theString
будет иметь принадлежащий Lua указатель (не удалять) на строку.strLen
также будет иметь длину строки.
Быстрый вывод: строки Lua заканчиваются NULL, но они также могут содержать символы NULL.C-строки не могут делать это, но C ++ std::string
s равны .Вот почему я не использую lua_tostring
так, как вы;Строки C ++ могут хранить строки Lua в том виде, в каком они есть.
Теперь, когда у нас есть строковые данные из Lua, нам нужно поместить их в наш список.Чтобы избежать ненужных копий, я предпочитаю следующий синтаксис:
list_strings.push_back();
list_strings.back().assign(theString, strLen);
Если бы я использовал стандартную библиотеку и компилятор с поддержкой C ++ 11, я бы просто использовал list_strings.emplace_back(theString, strLen);
, полагаясь на emplace_back
функция для создания std::string
на месте.Это аккуратно избегает создания большего количества копий строки, чем необходимо.
Есть еще один последний момент очистки, который нам нужно сделать.В нашем стеке по-прежнему есть два значения: строка и таблица.Мы закончили со строкой, поэтому нам нужно от нее избавиться.Это можно сделать, извлекая одну запись из стека Lua:
lua_pop(L, 1);
Здесь «1» - это количество записей для извлечения, а не расположение в стеке.
Вы понимаете, какУправление стеками работает в Lua сейчас?
1) Рассматривать состояние стека перед вызовом ... luaL_loadfile помещает функцию в стек?Или же lua_pcall?
Если вы ничего не сделали с состоянием Lua, кроме того, что создали его, то стек пуст до перед luaL_loadfile. И да, luaL_loadfile помещает функцию в стек. Эта функция представляет файл, который был загружен.
3) Каким будет результат стека, если после вызова функции он вернет значение ошибки?
Именно то, что говорится в документации. Теперь, когда вы понимаете, как работает стек, вы должны прочитать документы. Книга «Программирование на Lua» также рекомендуется. Версия 5.0 доступна онлайн бесплатно , но книга 5.1 стоит денег. Книга 5.0 - все еще полезная отправная точка.
4) list_strings.reserve (len); Что касается этого ... Этот скрипт lua на самом деле встроен в небольшую программу на C, которая рекурсивно просматривает кодовую базу и будет собирать ВСЕ строки, которые скрипт lua возвращает из ВСЕХ файлов ... Я не знаю точно, как резерв работает, но я хочу сказать, что я буду использовать много таблиц для добавления строк в этот список ... Должен ли резерв в этом случае не использоваться? или все еще используется ...
std::vector::reserve
гарантирует, что std::vector
будет содержать по крайней мере достаточно места для элементов X, где X - это значение, которое вы передаете. Я сделал это, потому что Lua сообщает вам, сколько элементов в таблице, поэтому нет необходимости позволять std::vector
расширяться самостоятельно. Вы можете сделать это одним выделением памяти для всего, вместо того, чтобы позволить std::vector::push_back
выделять больше памяти по мере необходимости.
Это полезно, если вы называете свой скрипт Lua один раз . То есть он получает единственное возвращаемое значение от Lua. Независимо от того, насколько велика возвращенная таблица, это будет работать. Если вы вызываете свой Lua-скрипт (из C ++) несколько раз, то заранее невозможно узнать, сколько памяти нужно зарезервировать. Вы можете зарезервировать место для каждой возвращаемой таблицы, но для схемы размещения по умолчанию std::vector
возможно опередить вас по количеству выделений для больших наборов данных. Так что в этом случае я бы не стал беспокоиться о reserve
.
Однако было бы неразумно начинать с reserve
здорового размера, как своего рода случай по умолчанию. Выберите число, которое, по вашему мнению, будет «достаточно большим», и зарезервируйте столько места.