Как создать таблицу в таблице в Lua 5.1, используя C-API?

Мне нужно создать такую ​​конструкцию в Lua 5.1 C-API, а не в Lua 5.2 и выше

a = {["b"] = {["c"] = {["d"] = {["e"] = "GOOD"}}}}

print(a.b.c.d.e);

Ожидаемый результат: ХОРОШО

Спасибо за ответы!

1 ответ

Решение

Lua C API основан на стеке. Это означает, что поведение большинства функций C API зависит не только от заданных аргументов, но и от содержимого стека Lua (который является частью lua_State* переменная обычно называется L). Что ожидает конкретная функция API в отношении содержимого стека и как оно влияет на элементы стека, вам нужно будет посмотреть в руководстве по Lua.

Давайте начнем с создания таблицы и присвоения ее глобальной переменной a:

lua_newtable( L );
lua_setglobal( L, "a" );

Это эквивалентно фрагменту кода Lua: a = {},

Документация для lua_newtable() говорит нам, что функция помещает новую таблицу в верхнюю часть стека Lua. Это хорошо вписывается в lua_setglobal(), который извлекает значение из вершины стека и присваивает его глобальной переменной с заданным именем. Таким образом, обе функции вместе (как в приведенном выше фрагменте) сбалансированы с точки зрения эффектов стека. Хорошая особенность кусочков кода, сбалансированных по стеку, заключается в том, что вы можете вставлять их в любое место, и объединенный код все еще действителен. (Общее правило: вы можете заменить один оператор на ряд операторов, и наоборот, если (комбинированные) эффекты стека одинаковы.) Например:

lua_newtable( L );  /* ==> stack: ..., {} */
lua_pushnil( L ); /* ==> stack: ..., {}, nil */
lua_pop( L, 1 ); /* ==> stack: ..., {} */
lua_setglobal( L, "a" ); /* ==> stack: ... */

будет по-прежнему назначать таблицу глобальной переменной a так как lua_pushnil( L ); а также lua_pop( L, 1 ); вместе не меняйте содержимое стека Lua. Вместо этого бесполезного нажатия / добавления мы добавим код, который изменяет таблицу после того, как она помещена в стек, и до того, как она будет удалена из стека и назначена глобальной переменной. Как я уже сказал, вы можете вставлять сбалансированные по стеку фрагменты кода в любое место между двумя функциями Lua C API, поэтому вам просто нужно определить правильное место, где в стеке содержатся все необходимые вам элементы.

Мы хотим добавить поле в таблицу с ключом "b" и другая таблица в качестве значения. Функция Lua C API для этого lua_settable() (есть другие вспомогательные функции, которые работают только для определенных типов клавиш, например, lua_setfield(), но мы пойдем с lua_settable() Вот). lua_settable() нужна таблица для хранения пары ключ / значение где- нибудь в стеке Lua (что обычно означает, что вы должны передать индекс стека в качестве аргумента), а также ключ и значение в качестве двух самых верхних элементов на стек. Ключ и значение (но не таблица) будут отображаться lua_settable():

lua_newtable( L );  /* ==> stack: ..., {} */
lua_pushliteral( L, "b" ); /* ==> stack: ..., {}, "b" */
lua_newtable( L ); /* ==> stack: ..., {}, "b", {} */
lua_settable( L, -3 ); /* ==> stack: ..., {} */
lua_setglobal( L, "a" ); /* ==> stack: ... */

Эквивалентный код Lua будет a = { b = {} }

Зачастую вас не волнует, что находится ниже определенной точки стека Lua, и вот где индексы относительно вершины стека (-3 в приведенном выше фрагменте кода) вступите в игру. -3 относится к таблице, которая находится чуть ниже ключа "b" (который находится в -2) и ниже другой таблицы (которая находится на вершине стека в -1).

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

lua_newtable( L );  /* ==> stack: ..., {} */
{
  lua_pushliteral( L, "b" ); /* ==> stack: ..., {}, "b" */
  lua_newtable( L ); /* ==> stack: ..., {}, "b", {} */
  {
    lua_pushliteral( L, "c" ); /* == stack: ..., {}, "b", {}, "c" */
    lua_newtable( L ); /* ==> stack: ..., {}, "b", {}, "c", {} */
    {
      lua_pushliteral( L, "d" );
      lua_newtable( L );
      {
        lua_pushliteral( L, "e" );
        lua_pushliteral( L, "GOOD" );
        lua_settable( L, -3 );
      }
      lua_settable( L, -3 );
    }
    lua_settable( L, -3 ); */ ==> stack: ..., {}, "b", {} */
  }
  lua_settable( L, -3 ); /* ==> stack: ..., {} */
}
lua_setglobal( L, "a" ); /* ==> stack: ... */

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

Другие вопросы по тегам