Comment enregistrer Lua Userdata corriger de Delphi?
-
26-10-2019 - |
Question
Je suis encore confus à propos de l'enregistrement Delphi userdata Lua. Pour me enseigner le principe j'ai essayé de mettre en œuvre un type de date (Time).
Au début, ce type devrait avoir trois fonctions accessibles à Lua:
- Une fonction
new
pour créer des variables de ce type. - Une fonction
getdate
. - et une fonction
setdate
.
A la fin de ce petit-Lua script devrait fonctionner:
DT = DateTime.new()
DT:setdate(1, 1, 2011)
day, month, year = DT:getdate()
print("Day: " .. day .. " Month: " .. month .." Year: " .. year)
J'ai essayé de le mettre en œuvre par moi-même (en utilisant le Programmation dans le livre de Lua) mais Je reçois une erreur disant:. _attempt to index global 'DT' (a userdata value)_
sur la ligne 2. Je probablement fait quelque chose de mal avec l'inscription userdata mais je vais avoir du mal à localiser l'erreur
J'espère que vous pouvez me aider à le trouver, voici ce que je suis déjà:
Const
MetaPosDateTime = 'DateTime';
Type
tLuaDateTime = tDateTime;
pLuaDateTime = ^tLuaDateTime;
Function newdatetime(aState : pLua_State) : longint; cdecl;
Var
NewData : pLuaDateTime;
Begin
Result := 0;
NewData := lua_newuserdata(aState, SizeOf(tLuaDateTime));
NewData^ := now;
luaL_newmetatable(aState, MetaPosDateTime);
lua_setmetatable(aState, -2);
Result := 1;
End;
Function setdate(aState : pLua_State) : longint; cdecl;
Var
DT : pLuaDateTime;
ParamType : integer;
day, month, year : lua_Integer;
Begin
Result := 0;
DT := luaL_checkudata(aState, 1, MetaPosDateTime);
luaL_argcheck(aState, DT <> Nil, 1, 'DataTime expected');
ParamType := lua_type(aState, 2);
If (ParamType = LUA_TTABLE) Then
Begin
{ GetData from Table }
End
Else
Begin // param order must be: day, month, year
day := luaL_checkinteger(aState, 2);
month := luaL_checkinteger(aState, 3);
year := luaL_checkinteger(aState, 4);
End;
DT^:= EncodeDate(year, month, day);
End;
Function getdate(aState : pLua_State) : longint; cdecl;
Var
DT : pLuaDateTime;
Day, Month, Year : Word;
Begin
DT := luaL_checkudata(aState, 1, MetaPosDateTime);
luaL_argcheck(aState, DT <> Nil, 1, 'DataTime expected');
DecodeDate(DT^, Year, Month, Day);
lua_pushinteger(aState, Day);
lua_pushinteger(aState, Month);
lua_pushinteger(aState, Year);
End;
Procedure RegisterDateTime(aState : pLua_State; aName: string);
Var
Funcs : packed Array[0..3] of luaL_reg;
Begin
Funcs[0].name := 'new';
Funcs[0].func := newdatetime;
Funcs[1].name := 'setdate';
Funcs[1].func := setdate;
Funcs[2].name := 'getdate';
Funcs[2].func := getdate;
Funcs[3].name := Nil;
Funcs[3].func := Nil;
luaL_register(aState, PAnsiChar(aName), Funcs[0]);
End;
Parce que je ne suis pas sûr de la fonction luaL_register (ne est-ce que le travail en créant une bibliothèque qui doit être appelée avec require
?) J'ai aussi essayé de remplacer la fonction RegisterDateTime
avec ceci:
Type
tLuaFuncDef = Record
FuncName : string;
Func : Lua_CFunction;
End;
tLuaFuncList = Array of tLuaFuncDef;
Procedure RegisterLuaObject(aState : pLua_State; aObjectName: string; aFuncList: tLuaFuncList);
Var
i : Integer;
Begin
If (aObjectName = '') Or (High(aFuncList) < 0) Then
Exit;
lua_newtable(aState);
For i := Low(aFuncList) To High(aFuncList) Do
If Assigned(aFuncList[i].Func) And Not (aFuncList[i].FuncName = '') Then
Begin
lua_pushcfunction(aState, aFuncList[i].Func);
lua_setfield(aState, -2, pAnsiChar(aFuncList[i].FuncName));
End;
lua_SetGlobal(aState, pAnsiChar(aObjectName));
End;
Procedure RegisterDateTime(aState : pLua_State, aName: string);
Var
FuncList : tLuaFuncList;
Begin
SetLength(FuncList, 3);
FuncList[0].FuncName := 'new';
FuncList[0].Func := newdatetime;
FuncList[1].FuncName := 'setdate';
FuncList[1].Func := setdate;
FuncList[2].FuncName := 'getdate';
FuncList[2].Func := getdate;
RegisterLuaObject(aState, aName, FuncList);
End;
Malheureusement, l'effet (errormessage;)) avec les deux versions de RegisterDateTime est le même. Ils sont appelés directement dans mon programme Delphi, avant le début de script (je me suis assuré en réglant les points d'arrêt dans « RegisterDateTime » et « newdatetime ». Les deux fonctions sont appelées dans cet ordre. Donc, mon erreur doit être dans l'un de ces deux fonctions. Je « m presque sûr qu'il est une chose simple mais je suis aveugle à le voir:. (
La solution
Aujourd'hui, je poussais la grande remise à zéro Bouton de ce projet et complètement remis en marche la mise en œuvre de mon LuaDateTime-type et ce jour-là je l'ai fait corriger. Maintenant, je veux poster ma solution comme par exemple pour quelqu'un d'autre qui a le même problème.
Ma plus grande erreur hier oubliais de définir le champ de __index
de mon métatable. Une implémentation Delphi travaille pour regards Lua de userdata comme ceci:
implementation
Uses
LuaLib,
LauXLib,
SysUtils;
Type
tLuaDateTime = tDateTime;
pLuaDateTime = ^tLuaDateTime;
Const
PosMetaTaleLuaDateTime = 'metatables.LuaDateTime';
PosLuaDateTime = 'datetime';
Function checkLuaDateTime(L : Plua_State) : pLuaDateTime; // raises error if first (self) parameter is not of type metatables.LuaDateTime
Begin
Result := luaL_checkudata(L, 1, PosMetaTaleLuaDateTime);
End;
Function newLuaDateTime(L : pLua_State) : LongInt; cdecl;
Var
a : pLuaDateTime;
Begin
a := lua_newuserdata(L, SizeOf(tLuaDateTime)); // Get Mem of Usertype
a^ := now; // Init Value
lua_getfield(L, LUA_REGISTRYINDEX, PosMetaTaleLuaDateTime);
lua_setmetatable(L, -2);
Result := 1;
End;
Function setLuaDateTime(L : pLua_State) : LongInt; cdecl;
Var
a : pLuaDateTime;
day, month, year : Integer;
Begin
a := checkLuaDateTime(L);
// get params day, month and year
day := luaL_checkint(L, 2);
month := luaL_checkint(L, 3);
year := luaL_checkint(L, 4);
// check Param Values
luaL_argcheck(L, (day >= 1) and (day < 32), 2, 'day out of range');
luaL_argcheck(L, (month >= 1) and (month < 13), 3, 'month out of range');
a^ := EncodeDate(year, month, day);
Result := 0;
End;
Function getLuaDateTime(L : pLua_State) : LongInt; cdecl;
Var
a : pLuaDateTime;
day, month, year : Word;
Begin
a := checkLuaDateTime(L);
DecodeDate(a^, year, month, day);
// push 3 results of function
lua_pushinteger(L, day);
lua_pushinteger(L, month);
lua_pushinteger(L, year);
Result := 3;
End;
Function LuaDateTime2string(L : pLua_State) : LongInt; cdecl;
Var
a : pLuaDateTime;
Begin
a := checkLuaDateTime(L);
lua_pushstring(L, pAnsiChar(FormatDateTime('c', a^)));
Result := 1;
End;
Const
LuaDateTimeLib_f : packed Array[0..1] of luaL_reg = // Normal functions (no self)
(
(name: 'new'; func: newLuaDateTime),
(name: Nil; func: Nil)
);
LuaDateTimeLib_m : packed Array[0..3] of luaL_reg = // methods of class (need self)
(
(name: '__tostring'; func: LuaDateTime2string),
(name: 'set'; func: setLuaDateTime),
(name: 'get'; func: getLuaDateTime),
(name: Nil; func: Nil)
);
Function luaopen_LuaDateTime(L : pLua_State) : LongInt; cdecl;
Begin
luaL_newmetatable(L, PosMetaTaleLuaDateTime);
// Metatable.__index = Metatable
lua_pushvalue(L, -1);
lua_setfield(L, -2, '__index');
luaL_register(L, Nil, LuaDateTimeLib_m[0]);
luaL_register(L,PosLuaDateTime, LuaDateTimeLib_f[0]);
Result := 1;
End;
Vous devez appeler luaopen_LuaDateTime
de Delphi pour enregistrer le type dans votre Lua-État. Après avoir fait que vous pouvez exécuter un script Lua-comme ceci:
dt = datetime.new()
day, month, year = dt:get()
print ("Day: " .. day .. " Month: " .. month .. " Year: " .. year)
dt:set(1, 2, 1903)
day, month, year = dt:get()
print ("Day: " .. day .. " Month: " .. month .. " Year: " .. year)
J'espère que cela est utile pour quelqu'un d'autre.