Как зарегистрировать Lua userdata правильно от Delphi?
-
26-10-2019 - |
Вопрос
Я до сих пор не понимаю, когда регистрирует Delphi UserData в LUA. Чтобы научить меня принципу, я пытался реализовать тип даты (время).
В начале этот тип должен иметь три функции, доступные для LUA:
- А
new
Функция для создания переменных этого типа. - А
getdate
функция - и
setdate
функция
В конце этот маленький Lua-Script должен работать:
DT = DateTime.new()
DT:setdate(1, 1, 2011)
day, month, year = DT:getdate()
print("Day: " .. day .. " Month: " .. month .." Year: " .. year)
Я пытался реализовать это самостоятельно (используя Программирование в Луа Книга) Но я получаю ошибку, в которой говорится: _attempt to index global 'DT' (a userdata value)_
В строке 2. Я, вероятно, сделал что -то не так с регистрацией UserData, но у меня возникли проблемы с поиском ошибки.
Я надеюсь, что вы сможете помочь мне найти это, вот что я уже получил:
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;
Потому что я не уверен в функции Lual_register (работает ли это только путем создания библиотеки, которая должна быть вызвана с require
?) Я также пытался заменить RegisterDateTime
функционируйте с этим:
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;
К сожалению, эффект (errormessage;)) с обеими версиями RegisterDateTime одинаково. Они называются прямыми в моей программе Delphi, до начала сценария (я обеспечил это, установив точки останова в «RegisterDateTime» и «NewDateTime». Обе функции вызываются в этом порядке. Поэтому моя ошибка должна быть в одной из этих двух функций. I Я почти уверен, что это простая вещь, но я слеп, чтобы увидеть это. :(
Решение
Сегодня я нажал кнопку «Большой сброс этого проекта» и полностью перезапустил реализацию моего типа LuadateTime, и в этот день я сделал это правильно. Теперь я хочу опубликовать свое решение в качестве примера для любого другого, у кого такая же проблема.
Моя самая большая ошибка вчера забыла установить __index
Поле моего метеотности. Рабочая реализация Delphi для Lua userData выглядит так:
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;
Вы должны позвонить luaopen_LuaDateTime
от Delphi, чтобы зарегистрировать тип в вашем штате Lua. После этого вы можете запустить Lua-Script, как это:
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)
Я надеюсь, что это полезно для кого -то другого.