Управление стеком с lua и C++

мне нужно передать Lua-скрипту одну строку (путь к файлу) и вернуть 0 ко многим строкам.

int error = 0;
lua_State *L = lua_open();
luaL_openlibs(L);

std::vector<string> list_strings;

используется для нажатия строки в стек, прежде чем я загружу и вызову исходный файл

if ((error = luaL_loadfile(L, "src/test.lua")) == 0)
{
    lua_pushstring(L, path.c_str());

    if ((error = lua_pcall(L, 1, LUA_MULTRET, 0)) == 0)
    {
        lua_gettable(L, LUA_GLOBALSINDEX);
        lua_pcall(L,1,1,0);

        if (lua_gettop(L) == 1 && lua_istable(L,1))
        {
            int len = lua_objlen(L,1);
            for (int i=1;i =< len; i++)
            {
                lua_pushinteger(L,i);
                lua_gettable(L,1);

                const char *s = lua_tostring(L,-1);
                if (s)
                {
                    list_strings.push_back(s);
                }

                lua_pop(L,1);
            }
        }
    }
}

как есть, я просто копировал код из примеров, поэтому я не совсем уверен, что то, что я делаю, - это то, что я хочу делать... Я хочу нажать путь в стек и вызвать функцию lua, которая берет это значение из стека, и проанализирует файл, который ассоциируется с этим путем.

после синтаксического анализа он должен вернуть таблицу, содержащую строки, которые находятся внутри нее (вы можете просто думать об этом как о функции, которая ищет определенную строку, я полагаю)

edit: сделано более ясно.

любые советы/ресурсы? Любые подобные вопросы здесь? или любые полезные ресурсы?

2 ответов


хорошо, давайте начнем сверху:

if ((error = lua_pcall(L, 1, LUA_MULTRET, 0)) == 0)

обычно, когда вы выполняете lua_pcall, третий параметр сообщает Lua ровно сколько возвращаемых значений ожидается. Если вызываемая функция возвращает больше этого числа, эти возвращаемые значения отбрасываются. Если он возвращает меньше этого числа, то для заполнения счетчика используются дополнительные нулевые значения.

LUA_MULTRET говорит Lua не делать этого. Когда это используется, все результаты помещаются в стек.

return "string1", "string2";

это приводит к тому, что 2 строки выталкиваются в стек. Это отличается от:

return {"string1", "string2"};

это ставит один объект в стеке: таблица. Таблица содержит 2 строки. Видишь разницу?

глядя на ваш код, кажется, что вы ожидаете, что сценарий Lua вернет стол of строки, а не несколько возвращаемых значений.

в этом случае вы должны вызвать свой сценарий Lua следующим образом:

if ((error = lua_pcall(L, 1, 1, 0)) == 0)

это говорит Lua, что вы ожидаете одно возвращаемое значение, и если пользователь не предоставит его, Lua будет нажимать ноль на стек.

теперь давайте поговорим о стеке. Состояние стека было таким перед выдачей вызова функции:

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}

однако, все, возможно, не все прошло хорошо. Возможно, сценарий вернул ноль. Или что-то еще; нет никакой гарантии, что это был стол. Итак, следующий шаг-проверить возвращаемое значение (Примечание: здесь ваш код перестает иметь смысл, так что это все новое).

if(lua_istable(L, -1))

lua_istable делает именно то, что предлагает название: определите, является ли данный элемент таблицей. Но что означает ли это" -1", и почему это не" 1", которое у вас было в коде?

этот аргумент является ссылкой на место в стеке. Стек Lua также является файлом регистра Lua. Это означает, что в отличие от реальные stack, вам разрешено пикировать на любом элементе в стеке. Элементы в стеке имеют абсолютное расположение в стеке. Теперь, вот как выглядит наш стек снова:

1- {return value}

что "1", который я написал, является абсолютным местоположением в стеке этого значение. Я могу нажать значения и pop значения, но если я не поп это значение, это местоположение будет всегда быть "1".

однако, это только "1", потому что наш стек начал пустой. Это несколько грубо предполагать это (так как он действительно может укусить вас, если стек не пуст. Документы Lua помогают указать, когда вы можете предположить, что стек действительно пуст, или если его нет, то, что уже находится в стеке). Поэтому можно использовать относительные местоположения.

и вот что такое "-1": это первый индекс стека из top стека. Наши!--17--> функция, как определено выше, будет выводить 2 элемента из стека (аргумент и функция) и нажимать 1 элемент (возвращаемое значение или ноль). Таким образом, "-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-базовые индексы в коде 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 также будет иметь длину строки.

Quick aside: строки Lua имеют нулевое завершение, но они также могут внутренне содержать нулевые символы. C-strings не разрешено делать это, но C++ std::strings are. Это почему я не использую 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" - это количество записей для pop, а не расположение стека.

вы понимаете, как управление стеком работает в Lua сейчас?


1) значение ошибки?

что именно в документации написано. теперь вы понимаете, как работает стек, вы должны прочитать документацию. Также рекомендуется программирование в Lua book. Версия 5.0-это доступно онлайн бесплатно, но книга 5.1 стоит денег. Книга 5.0 по-прежнему является полезной отправной точкой.

4) list_strings.резерв (лен); Что касается этого... Этот сценарий lua фактически встроен в небольшой C программа, которая повторяется через базовый код и будет собирать все ниточки, что возвращает скрипт Lua из всех файлов... Я не знаю точно, как работает reserve, но я говорю, что буду использовать много таблиц для добавления строк в этот список... Должен ли резерв просто не использоваться в этом случае? или все еще используется...

std::vector::reserve обеспечивает std::vector будет содержать по крайней мере достаточно места для X элементов, где X-это значение, которое вы передаете. Я сделал это, потому что Lua говорит вам, сколько элементов в таблице, поэтому нет необходимости, чтобы позволить std::vector расширить самостоятельно. Вы можете сделать это сделать одно выделение памяти для всего, а не позволять std::vector::push_back функция выделяет больше памяти по мере необходимости.

это полезно, пока вы называете свой скрипт Lua после. То есть, он получает одно возвращаемое значение из Lua. Независимо от того, насколько велика возвращенная таблица, это будет работать. Если вы вызываете сценарий Lua (с C++) несколько раз, тогда нет никакого способа узнать заранее, сколько памяти нужно зарезервировать. Вы можете зарезервировать место для каждой таблицы, которую вы вернете, но это возможно для std::vectorсхема распределения по умолчанию, чтобы побить вас по количеству распределений для больших наборов данных. Так что в этом случае я бы не стал беспокоиться о reserve.

однако было бы неразумно начинать со здорового размера reserve, как некий вариант по умолчанию. Выберите номер, который, по вашему мнению, будет "достаточно большим", и зарезервируйте столько пространство.


на стороне Lua нет стека. Значения, нажатые на стороне C, отправляются Lua в качестве аргументов для вызова. Если вы выполняете весь скрипт, а не конкретную функцию, аргументы доступны как .... Так что вы можете сделать local myarg = ... получить первый аргумент. Или local arg ={...}чтобы получить их все в таблицу.