-
Решил встроить Lua в свой проект и тут же наткнулся на то обстоятельство, что из скрипта не могу вызвать метод объекта. Т.е. могу (спс @!!ex и Servy) но несколько извращенным способом через чтение какого-нибудь свойства объекта.
procedure TLUAParser.AddObjectMethod(const MethodName: string; AObject: TObject;
const AObjectMethodName: string);
var
script: string;
p1: string;
begin
Inc(FLastBinderIndex);
lua_pushnumber(FLUAState, FLastBinderIndex);
lua_setglobal(FLUAState, 'Parser_LastBinderIndex');
p1 := IntToStr(LongInt(AObject));
script :=
MethodName + ' = function() ' +
' Parser_CallDelphiObjectsMethod(\"' + p1 + '\", \"' + AObjectMethodName + '\");' +
' return 1; ' +
' end ';
LoadFromMemory(script);
end;
function CallDelphiObjectsMethod(L: Plua_State): Integer; cdecl;
var
N: Integer;
O: TObject;
Method: AnsiString;
begin
N := lua_gettop(L);
if (N <> 2) then
raise ELuaException.CreateFmt('CallDelphiObjectsMethod failed. 2 arguments' +
' expected, but %d found.', [N]);
if (not lua_isstring(L, 1)) then
raise ELuaException.CreateFmt('Not a string type argument passed to ' +
'CallDelphiObjectsMethod. Received type is %s.', [lua_typename(L, lua_type(L, 1))]);
if (not lua_isstring(L, 2)) then
raise ELuaException.CreateFmt('Not a string argument passed to ' +
'CallDelphiObjectsMethod. Received type is %s.', [lua_typename(L, lua_type(L, 2))]);
O := TObject(StrToInt(string(lua_tostring(L, 1))));
Method := lua_tostring(L, 2);
if Assigned(O.MethodAddress(string(Method))) then
begin
ShowMessage(format('Method %s found at %s. All is ok :)',
[Method, O.ClassName]));
end
else
raise ELuaException.CreateFmt('Method %s not found at %s',
[Method, O.ClassName]);
Result := 1;
end;
...
FParser.AddObjectMethod('print', Form1, 'print');
При исполнении скрипта, в котором вызывается print(); появляется сообщение, что метод найден в нужном классе. Проблема в том, что я не умею вызвать метод объекта О по названию метода Method. Помнится, где-то на этом форуме встречал как это делается, но тема потерялась и всё забылось :( Собственно вопрос: Как вызвать метод, имея ссылку на объект и название метода. В случае если так сделать не удастся, есть еще вариант с глобальным (точнее "внутримодульным") массивом, в который будут храниться ссылки на методы. Тогда связывающая функция будет передавать не ссылку на объект и название метода, а только индекс из этого массива. Второй вариант не нравится из-за этого массива, как-то он не вписывается в концепцию ООП, хотя и работать вроде должно быстрее и проблем меньше.
-
Я ниче не понял по тексту и по коду. Могу только сказать как у меня сделано: lua_pushfunction(State,@ClassMethod);
lua_setglobal(State,'ClassMethod');
type
TMyClass = class
public
Procedure ClassMethod(Param:integer);
end;
procedure ClassMethod(State:TLUAState); stdcall;
var
Obj:TMyClass;
begin
Obj:=TMyClass(lua_tonumber(State,-2));
Obj.ClassMethod(trunc(lua_tonumber(State,-1)));
lua_pop(State,-2);
end;
вызов метода из скрипта: ClassMethod(MyClass,10); Как скрипт узнает MyClass - это другой вопрос. Лично я передаю его в функции.
-
Но ведь тебе на каждый метод надо писать отдельную функцию связки? По моему коду. Вот я задумал заменить функцию print на свой метод формы Form1.print. Я получаю ссылку на объект Form1: p1 := IntToStr(LongInt(AObject)); Название функции AObjectMethodName='print' у меня есть. В луа выполняю такой скрипт:
print = function ()
Parser_CallDelphiObjectsMethod(p1, AObjectMethodName);
return 1;
end
Таким образом стандартный print я заменяю на вызов своей функции CallDelphiObjectsMethod и в качетсве параметров передаю ссылку на объект: O := TObject(StrToInt(string(lua_tostring(L, 1)))); // Form1 и имя метода: Method := lua_tostring(L, 2); // "print" Теперь проблема в том, чтобы этот метод вызвать.
-
-
> [2] ggg © (22.12.08 05:15)
Не вижу проблем в функциях прослойках. Ето первое. Поиск метода в списке - это медленно. Ето второе. Мой вариант буедт быстрее. и при большом количестве методов - заметно быстрее.
> [3] ggg © (22.12.08 05:54)
У меня тоже сначала положительные числа стояли... Потом выяснилось что это не работает... где то увидел что отрицательные. заменил. заработало.
-
> У меня тоже сначала положительные числа стояли... Потом > выяснилось что это не работает... где то увидел что отрицательные. > заменил. заработало.
Это не говорит, что "-" это правильно. Вполне возможно что это НЕ правильно. Слишком мало я еще ханимаюсь Луа.
-
Да, мой вариант помедленнее, зато универсальнее. Если скорость не критична, вполне можно использовать.
unit LUAParser;
type
TLUAExternObjectMethodAddress = function (const P: TLUAParser): Integer of object;
TLUAExternObjectMethod = record
Name: string;
Parser: TLUAParser;
ParamCount: Integer;
Address: TLUAExternObjectMethodAddress;
end;
...
var
ParserExternObjectMethods: array of TLUAExternObjectMethod;
..
function CallDelphiObjectsMethod(L: Plua_State): Integer; cdecl;
var
N: Integer;
methodIndex: Integer;
begin
N := lua_gettop(L);
if (N < 1) then
raise ELuaException.CreateFmt('CallDelphiObjectsMethod failed. 1 argument' +
' expected, but %d found.', [N]);
if (not lua_isnumber(L, -1)) then
raise ELuaException.CreateFmt('Not a number type argument passed to ' +
'CallDelphiObjectsMethod. Received type is %s.', [lua_typename(L, lua_type(L, -1))]);
methodIndex := trunc(lua_tonumber(L, -1));
if (0 <= methodIndex) and (methodIndex < Length(ParserExternObjectMethods)) then
begin
lua_pop(L, 1);
with ParserExternObjectMethods[methodIndex] do
if Assigned(Address) then
Address(Parser);
end
else
raise ELuaException.CreateFmt('Method %d not found', [methodIndex]);
Result := 1;
end;
...
procedure TLUAParser.AddObjectMethod(const MethodName: string;
const AMethod: TLUAExternObjectMethodAddress;
const ParamCount: Integer);
var
script: string;
prmStr: string;
i, n: Integer;
methodIndex: Integer;
function ifNotNull(const s: string): string;
begin
if (s <> '') then
Result := ','
else
Result := ''
end;
begin
methodIndex := Length(ParserExternObjectMethods);
SetLength(ParserExternObjectMethods, methodIndex + 1);
ParserExternObjectMethods[methodIndex].Name := MethodName;
ParserExternObjectmethods[methodIndex].Parser := Self;
ParserExternObjectmethods[methodIndex].Address := AMethod;
ParserExternObjectmethods[methodIndex].ParamCount := ParamCount;
n := Length(FExternObjectMethods);
SetLength(FExternObjectMethods, n + 1);
FExternObjectMethods[n] := methodIndex;
if ParamCount > 0 then
begin
prmStr := 'p1';
for i := 2 to ParamCount do
prmStr := prmStr + ',p' + IntToStr(i);
end
else
prmStr := '';
script :=
MethodName + ' = function(' + prmStr + ') ' +
' return Parser_CallDelphiObjectsMethod(' + prmStr +
ifNotNull(prmStr) + IntToStr(methodIndex) + ');' +
' end ';
LoadFromMemory(script);
end;
С lua_pop вроде всё нормально, убирает n последних элементов из стека.
-
> Проблема в том, что я не умею вызвать метод объекта О по > названию метода Method. Помнится, где-то на этом форуме > встречал как это делается, но тема потерялась и всё забылось > :(
В меру моего недосконального знания, Делфи позволяет runtime получить указатель на адрес метода по имени. Однако, чтобы вызвать метод, ему надо передать параметры с использованием calling conversion, принятой этим методом, а сделать это правильным образом не представляется возможным, так как храниться только адрес метода, но не его параметры и способ их передачи. Соответственно (возможно бесполезное) желание порулить делфевыми объектами из lua полностью успехом увенчаться не может. Однако, есть альтернативные варианты, одну из вариаций вы изложили в [6], с "ручной" регистрацией всех нужных методов (для каждого метода нужно вызвать AddObjectMethod). Однако, реализация в [6] довольно странная: 1. Там регистрируется не просто метод объекта, а метод в связке с объектом. TLUAExternObjectMethodAddress занимает 8 байт, хранит указатель на экземпляр класса, для которого вызывать метод (который передавать в Self), и адрес собственно метода. Таким образом, я могу вызывать метод print только для одного экземпляра - какое же это ООП? 2. Список параметров странный. Как будет выглядеть вызов зарегестрированного TForm1.print() в луа? Судя по всем "print(text);" - тоже совсем не похоже на ООП, объект (экземпляр) отсутствует :). 3. Все методы получают по сути один параметр - окружение луа, а настоящие параметры должны оттуда выковыривать сами, что не представляется удобным). Так что, решение [1] выглядит более разумным. Еще бы генератор прослоек написать, чтобы все время руками их не набивать ;). Да как т.н. эксперт в Делфи встроить, чтобы по хоткею переходники генерировала, было б удобно. И Obj:=TMyClass(lua_tonumber(State,-2)); tonumber немного смущает, видимо объект по сути представляет число в типах lua. А для числа определены, например, сложение и вычитание :). Понятно, что если к числу, которое на самом деле указатель, прибавить что-то, то есть хороший шанс в программе словить AV. Ясно, конечно, что скриптописатель "сам дурак", однако есть возможность непозволить ему напортачить - тип userdata в луа, все операции для которого вы устанавливаете (или не устанавливаете) самостоятельно. > вызов метода из скрипта: > ClassMethod(MyClass,10);
Если я правильно помню мануал по луа, то эта запись эквивалентна следующей: MyClass.ClassMethod(10); Этот вариант записи в контексте ООП выглядит более привычно.
-
> Если я правильно помню мануал по луа, то эта запись эквивалентна > следующей: > > MyClass.ClassMethod(10); > > Этот вариант записи в контексте ООП выглядит более привычно.
Вот только MyClass не является классом с точки зрения Lua, так что такая запись работать не будет.
-
Хотя стоп. Может я и не прав... Проверю. Только не MyClass.ClassMethod(10); а MyClass:ClassMethod(10);
-
> [9]
Да нет, судя по всему это меня подвела память.
A call v:name(args) is syntactic sugar for v.name(v,args), except that v is evaluated only once.
Таким образом, v должен быть либо таблицей, у которого есть ключ name со значением типа function, либо для v должен быть перекрыт "index" метаметод, который будет возвращать нужную функцию. То есть, без дополнительных ухищрений, такой синтаксис использовать нельзя.
-
> [10] Servy © (23.12.08 15:22)
Да, именно это я и имел ввиду.
-
В принципе ничего не мещает передавать MyClass не как указатель на класс, а как таблицу. Сути это сильно не поменяет. Только какую функцию вызывать будет уже решать таблица, а не сам кодер.
Тогда код функции будет выглядеть немного страшно: function TMyClass:ClssMethod(Value) TMyClass_ClassMethod(self.classPointer, Value); end;
Зато вызов вполне себе красиво: MyClass:ClassMethod(10); И все. красота.
Однако добавление метода в Lua уже не будет таким тривиальным. Потому что надо будет построить таблицу и ее передать...
-
|