2009-04-07 2 views
6

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

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

+0

Кажется, что у lua 5.2 есть то, что вы ищете: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

ответ

3

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

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

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

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

0

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

5

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

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 можно использовать для выделения нескольких связанных метатегов.

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

2

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

Вы можете отправить этот тип в код 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");