2009-04-07 23 views
6

Mam obiekt Lua userdata z pewnym typem metatabilnym (np. "stackoverflow.test"). Z kodu C chcę móc dokładnie sprawdzić, jaki to jest, i zachowywać się inaczej w zależności od wyników. Czy jest miła przydatna funkcja (raczej jak luaL_checkudata, ale bez błędu, jeśli odpowiedź nie jest tym, czego potrzebujesz), to pozwól mi zapytać metatabilną nazwę typu danych użytkownika? Jeśli nie, to potrzebuję użyć lua_getmetatable, ale jestem niejasny, jak ustalam nazwę metatable, która właśnie została dodana do stosu.Typ danych użytkownika Query Lua z C

Aby wyjaśnić: Używam Lua 5.1, gdzie zmieniono zachowanie luaL_checkudata. Rozumiem, że w 5.0 nie używał błędu.

+0

Wygląda na to, że lua 5.2 ma to, czego szukasz: [luaL_testudata] (http://www.lua.org/source/5.2/lauxlib.c.html#luaL_testudata) –

Odpowiedz

3

Będziesz korzystać lua_getmetatable i lua_equal do testowania, że ​​tabele są takie same.

Moim zdaniem, Lua powinna udzielać większego wsparcia dla tego rodzaju rozszerzeń typu. W tej chwili to naprawdę leży w gestii systemu owijającego Lua/C (++).

W opakowaniu, które zrobiłem ostatnio (w ramach komercyjnego projektu), wykonuję class::instance(L,index), aby uzyskać wskaźniki userdata określonego typu. Innymi słowy, ta metoda sprawdza, czy są to dane użytkownika i czy metatabela ma również rację. Jeśli nie, zwraca NULL.

Sposób, w jaki Lua może pomóc w rozwiązaniu tego problemu, to fakt, że metatable zawierało standardowe pole dla rozszerzonych informacji o typie (s.a. __type). To może być użyte tak, aby sam type() zwrócił "userdata", "xxx" (dwie wartości, obecnie zwracające tylko jeden). Pozostanie to zgodne z większością obecnego kodu. Jest to jednak hipotetyczne (chyba że zrobisz niestandardowy typ() i wdrożysz to na własną rękę).

0

Właśnie przyjrzałem się kodowi źródłowemu funkcji luaL_checkudata i zasadniczo pobieram metatabel obiektu obiektu użytkownika za pomocą lua_getmetatable. Następnie pobiera nazwę określonego typu z rejestru przy użyciu lua_getfield i wykonuje wywołanie lua_rawequal, aby je porównać.

5

Zawsze można zapisać pole znacznika w metatabelku z lekką wartością userdata unikalną dla twojego modułu.

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; 
} 

Następnie można użyć my_setflavor(L,&green_flavor) aby ustawić pole _flavor tabeli na górze stosu i my_isflavor(L,&red_flavor) przetestować pole _flavor tabeli na górze stosu.

Użyte w ten sposób pole _flavor może przyjmować tylko wartości, które mogą być utworzone przez kod w module, który ma znak green_flavor w zakresie, a wyszukanie pola i przetestowanie jego wartości wymaga tylko jednego wyszukiwania tabeli poza pobieraniem samego metatable. Zauważ, że wartość zmiennej green_flavour nie ma znaczenia, ponieważ tylko jej adres jest faktycznie używany.

Z kilkoma różnymi zmiennymi smakowymi dostępnymi do wykorzystania jako wartości sentencji, pole _flavor może być użyte do rozróżnienia kilku powiązanych metatabli.

Wszystko to powiedziawszy, naturalnym pytaniem jest "po co w ogóle to robić?" W końcu metatable może łatwo zawierać wszystkie informacje potrzebne do uzyskania odpowiedniego zachowania. Może on łatwo przechwytywać funkcje, jak również dane, a funkcje te mogą być wywoływane i wywoływane z C, jak również z Lua.

2

Dane użytkownika muszą być sparowane, więc chwyć to; następnie wyszukaj żądane nazwisko w rejestrze. Jeśli oba obiekty są identyczne, znajdziesz typ, którego szukasz.

Możesz wysyłać na ten typ kodu C, ale pozwól mi delikatnie zasugerować, aby zamiast tego wskazać pole metatabeli. Funkcja zapisana w metatabelku powinna wykonać zadanie, ale jeśli nie, to koniecznie musisz mieć switch w kodzie C, następnie wybierz nazwę, użyj jej do zindeksowania w metatable i przypisz każdemu metatablowi małą liczbę całkowitą, którą możesz włączyć.

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

a następnie w kodzie 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");