Wie registriere ich Lua userData korrekt von Delphi?
-
26-10-2019 - |
Frage
Ich bin immer noch verwirrt, Delphi UserData an Lua zu registrieren. Um mir das Prinzip beizubringen, habe ich versucht, einen Datumstyp (Uhrzeit) umzusetzen.
Am Anfang sollte dieser Typ drei Funktionen für lUA zugänglich sein:
- EIN
new
Funktion zum Erstellen von Variablen dieses Typs. - EIN
getdate
Funktion. - und ein
setdate
Funktion.
Am Ende sollte dieses kleine Lua-Script funktionieren:
DT = DateTime.new()
DT:setdate(1, 1, 2011)
day, month, year = DT:getdate()
print("Day: " .. day .. " Month: " .. month .." Year: " .. year)
Ich habe versucht, es alleine zu implementieren (mit der Programmierung in Lua Buch) Aber ich erhalte einen Fehler, der sagt: _attempt to index global 'DT' (a userdata value)_
In Zeile 2. Ich habe mit der Registrierung von UserData wahrscheinlich etwas falsch gemacht, aber ich habe Probleme, den Fehler zu finden.
Ich hoffe, Sie können mir helfen, es zu finden, hier ist, was ich schon habe:
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;
Weil ich mich über die Funktion lual_register nicht sicher bin (funktioniert sie nur, indem sie eine Bibliothek erstellt, die aufgerufen werden muss require
?) Ich habe auch versucht, das zu ersetzen RegisterDateTime
Funktionieren Sie damit:
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;
Leider ist der Effekt (ErrorMessage;)) mit beiden Versionen von RegisterDatetime gleich. Sie werden in meinem Delphi -Programm direkt bezeichnet, bevor das Skript beginnt (ich habe dies sichergestellt, indem ich Breakpoints in "RegisterDatetime" und "NewDatetime" einstellte. Beide Funktionen werden in dieser Reihenfolge aufgerufen. Mein Fehler muss also in einer dieser beiden Funktionen sein. Ich bin mir fast sicher, dass es eine einfache Sache ist, aber ich bin blind, um es zu sehen. :(
Lösung
Heute habe ich den großen Reset-Knopf dieses Projekts gedrückt und die Implementierung meines Luadatetime-Typs vollständig neu gestartet und heute habe ich es richtig gemacht. Jetzt möchte ich meine Lösung als Beispiel für jeden anderen veröffentlichen, der das gleiche Problem hat.
Mein größter Fehler gestern war, das zu vergessen, das festzulegen __index
Feld meines Versenkers. Eine funktionierende Delphi -Implementierung für Lua UserData sieht folgendermaßen aus:
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;
Sie müssen anrufen luaopen_LuaDateTime
von Delphi, um den Typ in Ihrem Lua-Staat zu registrieren. Danach können Sie ein Lua-Skript wie diesen ausführen:
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)
Ich hoffe, das ist hilfreich für jemand anderen.