Запрос типа пользовательских данных Lua из C - PullRequest
6 голосов
/ 07 апреля 2009

У меня есть объект пользовательских данных Lua с определенным метатабельным типом (например, "stackoverflow.test"). Из кода C я хочу иметь возможность точно проверить, какой это тип, и вести себя по-разному в зависимости от результатов. Есть ли полезная функция (скорее как luaL_checkudata, но без ошибок, если ответ не тот, который вам нужен), который позволяет мне запрашивать метатабельное имя типа userdata? Если нет, думаю, мне нужно использовать lua_getmetatable, но тогда мне немного неясно, как определить имя метатаблицы, только что добавленной в стек.

Просто чтобы уточнить: я использую Lua 5.1, где изменилось поведение luaL_checkudata. Я понимаю, что в 5.0 не было ошибок.

Ответы [ 4 ]

5 голосов
/ 08 апреля 2009

В метатаблице всегда можно сохранить поле маркера со светлым значением пользовательских данных, уникальным для вашего модуля.

static const char *green_flavor = "green";
...
void my_setflavor(lua_State *L, void *flavor) {
  lua_pushlightuserdata(L,flavor);
  lua_pushlstring(L,"_flavor");
  lua_rawset(L,-3);
}

void my_isflavor(lua_State *L, void *flavor) {
  void *p = NULL;
  lua_pushlstring(L,"_flavor");
  lua_rawget(L,-2);
  p = lua_touserdata(L,-1);
  lua_pop(L,1);
  return p == flavor;
}

Затем вы можете использовать my_setflavor(L,&green_flavor) для установки поля _flavor таблицы в верхней части стека и my_isflavor(L,&red_flavor) для проверки поля _flavor таблицы в верхней части стека.

При использовании этого способа поле _flavor может принимать только те значения, которые могут быть созданы кодом в модуле с символом green_flavor в области видимости; для поиска поля и проверки его значения требуется только один поиск в таблице, кроме поиска сам метатабельный. Обратите внимание, что значение переменной green_flavor не имеет значения, поскольку фактически используется только ее адрес.

При наличии нескольких различных переменных вкуса, доступных для использования в качестве часовых значений, поле _flavor можно использовать для различения нескольких связанных метатаблиц.

Все это говорит, естественный вопрос: "Зачем вообще это делать?" В конце концов, метатабель может легко содержать всю информацию, необходимую для получения соответствующего поведения. Он может легко содержать функции, а также данные, и эти функции могут быть извлечены и вызваны из C, а также Lua.

3 голосов
/ 09 апреля 2009

Вы будете использовать lua_getmetatable и lua_equal для проверки того, что таблицы совпадают.

По-моему, Луа должен больше поддерживать такие вещи, которые расширяют тип. На данный момент ответственность за это лежит на системе-оболочке Lua / C (++).

В оболочке, которую я недавно сделал (как часть коммерческого проекта), я делаю class::instance(L,index), чтобы получить указатели пользовательских данных определенного типа. Другими словами, этот метод проверяет, что это пользовательские данные и что метатабель также является правильным. Если нет, возвращается NULL.

Lua мог бы помочь во всем этом, если бы у metatable было стандартное поле для расширенной информации о типах (s.a. __type). Это можно использовать так, чтобы type() сам возвращал «userdata», «xxx» (два значения, в настоящее время возвращающее только одно). Это останется совместимым с большей частью текущего кода. Но это только гипотетически (если вы не сделаете пользовательский тип () и не реализуете его самостоятельно).

2 голосов
/ 09 апреля 2009

Пользовательские данные должны иметь метатабельность, так что хватайте их; затем найдите нужное имя в реестре. Если два объекта идентичны, вы нашли искомый тип.

Вы можете отправить этот тип в C-коде, но позвольте мне мягко предложить вместо этого указать поле метатаблицы. Функция, хранящаяся в метатабле, должна выполнять эту работу, но если нет, если вам абсолютно необходимо switch в коде C, затем выберите имя, используйте его для индексации в метатабле и назначьте каждому метатабилю небольшое целое число, которое вы можете переключить. на.

meta1.decision = 1
meta2.decision = 2
meta3.decision = 3

, а затем в вашем коде C

if (lua_getmetatable(L, 1)) {
  lua_getfield(L, -1, "decision");
  if (lua_isnumber(L, -1)) {
    switch ((int) lua_tonumber(L, -1)) {
       case 1: ... ; break;
       case 2: ... ; break;
       case 3: ... ; break;
    }
    return 0;
  }
}
return luaL_error(L, "Userdata was not one of the expected types");
0 голосов
/ 07 апреля 2009

Я только что посмотрел на исходный код функции luaL_checkudata, и он в основном выбирает объект метаданных объекта userdata, используя lua_getmetatable. Затем он получает указанное имя типа из реестра, используя lua_getfield, и вызывает lua_rawequal, чтобы сравнить их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...