Конференция "Прочее" » Оценил простоту и мощь Lua
 
  • @!!ex © (10.12.08 17:56) [0]
    Вчера занялся внедрением скриптового движка в продукт.
    Остановился на Lua, как мощном и гибком движке.

    Сделал классовую обертку(может кому понадобится?). Покажите на ошибки, где накосячил?

    unit uLua;

    interface
    uses lua, lualib, lauxlib,
        Dialogs;

    type
     TLUAExternMethod = record
       Name:string;
       Address:lua_CFunction;
     end;

     TLUAParser = class
     protected
       FLUAState:Pointer;
       FExternMethods:array of TLUAExternMethod;
       Procedure   LuaInit;
       Procedure   LuaClose;
       Procedure   LuaUpdateMethods;
       Procedure   LuaAddMethod(const MethodName:string; Method:lua_CFunction);
     public
       Constructor Create();
       Destructor  Destroy(); override;
       Procedure   Clear();
       Procedure   LoadFromMemory(const Text:string);
       Function    Call(const ProcName:string; Params:array of const):boolean;
       Function    CallResultInt(const ProcName:string; Params:array of const):integer;
       Function    CallResultDoudble(const ProcName:string; Params:array of const):double;
       Function    CallResultString(const ProcName:string; Params:array of const):string;
       Procedure   AddMethod(const MethodName:string; Method:lua_CFunction);
     end;

    implementation

    { TLUAParser }

    procedure TLUAParser.AddMethod(const MethodName: string; Method: lua_CFunction);
    begin
     SetLength(FExternMethods,Length(FExternMethods)+1);
     FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
     FExternMethods[Length(FExternMethods)-1].Address:=Method;
     LuaAddMethod(MethodName,Method);
    end;

    Function TLUAParser.Call(const ProcName: string; Params: array of const):boolean;
    var
     i:integer;
    begin
     Result:=true;
     lua_getglobal(FLUAState, PChar(ProcName));
     for i := 0 to Length(Params) - 1 do begin
       case Params[i].VType of
         vtExtended:lua_pushnumber(FLUAState, Params[i].vExtended^);
         vtInteger:lua_pushnumber(FLUAState, Params[i].VInteger);
         vtString:lua_pushstring(FLUAState, PChar(Params[i].VString));
         vtPointer:lua_pushnumber(FLUAState, Params[i].VInteger);
       end;
     end;
     if (lua_pcall(FLUAState, Length(Params), 0, 0) <> 0) then begin
       ShowMessage('error running function '+ProcName+': '+lua_tostring(FLUAState, -1));
       lua_pop(FLUAState, 1);
       Result:=false;
     end;
    end;

    function TLUAParser.CallResultDoudble(const ProcName: string;
     Params: array of const): double;
    begin
     if Call(ProcName,Params) then begin
       Result:=lua_tonumber(FLUAState,-1);
       lua_pop(FLUAState, 1);
     end;
    end;

    function TLUAParser.CallResultInt(const ProcName: string;
     Params: array of const): integer;
    begin
     if Call(ProcName,Params) then begin
       Result:=Round(lua_tonumber(FLUAState,-1));
       lua_pop(FLUAState, 1);
     end;
    end;

    function TLUAParser.CallResultString(const ProcName: string;
     Params: array of const): string;
    begin
     if Call(ProcName,Params) then begin
       Result:=lua_tostring(FLUAState,-1);
       lua_pop(FLUAState, 1);
     end;
    end;

    procedure TLUAParser.Clear;
    begin
     LuaClose();
     LuaInit();
     LuaUpdateMethods;
    end;

    constructor TLUAParser.Create;
    begin
     LuaInit();
    end;

    destructor TLUAParser.Destroy;
    begin
     LuaClose();
     inherited;
    end;

    procedure TLUAParser.LoadFromMemory(const Text: string);
    var
     error:integer;
    begin
     error:=luaL_loadbuffer(FLUAState, PChar(Text), length(Text), 'Chunk');
     if error=0 then
       error:=lua_pcall(FLUAState, 0, 0, 0);
     if (error<>0) then begin
       ShowMessage(lua_tostring(FLUAState, -1));
       lua_pop(FLUAState, 1);
     end
    end;

    procedure TLUAParser.LuaAddMethod(const MethodName: string;
     Method: lua_CFunction);
    begin
     lua_pushcfunction(FLUAState, Method);
     lua_setglobal(FLUAState, PChar(MethodName));
    end;

    procedure TLUAParser.LuaClose;
    begin
     lua_close(FLUAState);
    end;

    procedure TLUAParser.LuaInit;
    begin
     FLUAState:=lua_open();
     luaopen_base(FLUAState);
     luaopen_table(FLUAState);
     luaopen_string(FLUAState);
     luaopen_math(FLUAState);
    end;

    procedure TLUAParser.LuaUpdateMethods;
    var
     i:integer;
    begin
     for I := 0 to Length(FExternMethods) - 1 do
       LuaAddMethod(FExternMethods[i].Name,FExternMethods[i].Address);
    end;

    end.

  • Palladin © (11.12.08 09:01) [1]

    > CallResultDoudble

    ненравится мне этот тип :)
  • wicked © (11.12.08 12:10) [2]
    LoadFromStream/SaveToStream где?
  • @!!ex © (11.12.08 13:50) [3]
    > [1] Palladin ©   (11.12.08 09:01)

    мда уж... опечатался. :))


    > [2] wicked ©   (11.12.08 12:10)

    а зачем?
    Из текстовой строки грузит. а как эту строки доставали - ваше дело.
    Меньше привязки к юнитам.
  • clickmaker © (11.12.08 13:56) [4]
    > Меньше привязки к юнитам.

    тогда уж убери Dialogs и ShowMessage
    а вместо
    ShowMessage('error running function '+ProcName+': '+lua_tostring(FLUAState, -1));
    вызывай событие
    if Assigned(FOnError) then
     FOnError('error running function '+ProcName+': '+lua_tostring(FLUAState, -1));
  • Alkid (11.12.08 14:50) [5]

    > @!!ex ©   (11.12.08 13:50) [3]

    Ну, поток - это более высокий уровень абстракции, чем строка.
    Реюзабельность класса от этого повысится.
  • tesseract © (11.12.08 15:25) [6]
    А как поначалу LUA оскоблял. Теперь заценил вот :-)


    > Реюзабельность класса от этого повысится.


    Тогда уже сразу абстракный  Stream вешать, можно и с нета и из архива напрямую качать.
  • Alkid (11.12.08 18:04) [7]

    > tesseract ©   (11.12.08 15:25) [6]

    Конечно абстрактный!
  • wicked © (11.12.08 18:07) [8]
    TStream и имелся в виду
    на что-либо выше по иерархии завязываться нет смысла
  • @!!ex © (11.12.08 21:32) [9]
    > [4] clickmaker ©   (11.12.08 13:56)

    Спасибо.


    > [5] Alkid   (11.12.08 14:50)

    В данном случае код из моего проекта, котоырй сейчас делаю. А в нем Стримы вообще не используются...

    > [6] tesseract ©   (11.12.08 15:25)

    Не ругал я Lua никогда! :)
    В студию мои ругательства в сторону Lua!
    говорил, что не имеет смысла использовать в своих проектах, потому что использование чужого кода не учит писать свой.
  • Городской Шаман (12.12.08 00:24) [10]

    >
    > @!!ex ©   (10.12.08 17:56)


    Ты ж вроде на С++ перешёл?
  • @!!ex © (12.12.08 09:41) [11]
    > [10] Городской Шаман   (12.12.08 00:24)

    Есть на С++ проекты. Но это не отменило Дельфи проектов. :)
  • VMcL © (12.12.08 11:43) [12]
    procedure TLUAParser.AddMethod(const MethodName: string; Method: lua_CFunction);
    begin
    SetLength(FExternMethods,Length(FExternMethods)+1);
    FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
    FExternMethods[Length(FExternMethods)-1].Address:=Method;
    LuaAddMethod(MethodName,Method);
    end;


    За такое перераспределение памяти уволить без выходного пособия.
  • @!!ex © (12.12.08 12:30) [13]
    > [12] VMcL ©   (12.12.08 11:43)

    Ваши предложения?
  • @!!ex © (12.12.08 12:33) [14]
    Насколько я вижу, добавление элементов в C++ vector через push_back также дефрагментирует память. Разве есть другие варианты?
    List чтоли делать? Или выделять сразу блоками? Эти способы тоже не панацея.
  • Palladin © (12.12.08 12:34) [15]
    TStringList
  • clickmaker © (12.12.08 12:35) [16]
    > SetLength(FExternMethods,Length(FExternMethods)+1);
    > FExternMethods[Length(FExternMethods)-1].Name:=MethodName;
    >
    > FExternMethods[Length(FExternMethods)-1].Address:=Method;

    Len := Length(FExternMethods) + 1;
    SetLength(FExternMethods,Len);
    Dec(Len, 2);
    FExternMethods[Len].Name:=MethodName;
    FExternMethods[Len].Address:=Method;

    на самом деле, можно не каждый раз увеличивать массив на 1, а сразу задать некую длину, и если при добавлении она превышена, перераспределить заново на некую дельту. По аналогии с Capacity в TList
  • @!!ex © (12.12.08 12:35) [17]
    > [15] Palladin ©   (12.12.08 12:34)

    Еще один "левый" класс. Ради чего?
    И разве он также не делает realloc?
  • clickmaker © (12.12.08 12:36) [18]
    > Dec(Len, 2);

    Dec(Len), конечно же )
  • @!!ex © (12.12.08 12:37) [19]
    > [16] clickmaker ©   (12.12.08 12:35)

    Я так и делаю, в местах где данные меняются часто и критично по скорости.
    Здесь скорость не критична, и данные меняются только один раз при инициализации.
  • clickmaker © (12.12.08 12:37) [20]
    > И разве он также не делает realloc?

    не на каждое добавление
    см. Capacity
  • Palladin © (12.12.08 12:38) [21]

    > @!!ex ©   (12.12.08 12:35) [17]

    Ни TList ни TStringList не работают с дин. массивами, у них своя схема. Как раз блоками.
  • @!!ex © (12.12.08 12:39) [22]
    > [20] clickmaker ©   (12.12.08 12:37)

    Я видел как сделан TList. :)
    В [14] об этом написал.

    > Или выделять сразу блоками?

    Смысла не вижу в данном случае.
  • Palladin © (12.12.08 12:41) [23]
    Ну на самом деле, все зависит от ожидаемого количествоа этих Extern методов. Если не больше 100, можно сразу размерность выделить 100. И не нагружать менеджер Reallocaми
  • Городской Шаман (12.12.08 13:58) [24]

    > VMcL ©   (12.12.08 11:43) [12]


    Это те 90% кода которые влияют на 1% производительности. Так что конструкция вполне легитимная, сам так делаю. Данный код при регистрации всех функций не будет на современном процессоре и 0.1 мс забирать.

    Так что я против premature optimization, которая приводит к шаманской работе программ - то она работает, то нет.
  • Городской Шаман (12.12.08 14:05) [25]

    > Palladin ©   (12.12.08 12:41) [23]


    Угу, только при подобных оптимизация получится модуль с производительность выше на 5-10% который будет влиять на 0.0001% производительности программы, с читабельностью кода стремящейся к 0%.

    Сам говорю по опыту, тоже пробовал ловить проценты производительности там где не надо. Если так уж важна производительность то уд лучше перевести проект на C++ с нормальным компилятором, а не на тормознутом Ынтерпрайзе Delphi. Даже перекомпиляция на современном FPC увеличивает часто производительность в 300%.

    Это конечно хорошо для того, кто хочет завязать проект на себя, но плохо для заказчика.
  • Palladin © (12.12.08 14:12) [26]

    >  с читабельностью кода стремящейся к 0%.

    Хм, а как повредит единоразовое определение размера дин. массива на читабельность кода?
  • Servy © (12.12.08 14:28) [27]
    > лучше перевести проект на C++ с нормальным компилятором,
    > а не на тормознутом Ынтерпрайзе Delphi


    Зависит от критерия "нормальности". Для меня, компилятор, собирающий среднего размера проект минутами, нормальным не является :). Но, на вкус и цвет...

    > Сделал классовую обертку(может кому понадобится?).


    Мне понадобилось, давно собирался посмотреть, что за зверь такой этот lua, а тут повод появился. Thanks :). Взял на себя смелость внести некоторые изменения в приведенный модуль:

    [*] Незначительные изменения для совместимости с D2009 (экспериментировал на ней). В частности, string -> AnsiString, там где нужно.
    [+] Добавил "Adapter" делфевых объектов. В двух словах, теперь можно легко написать функцию, которая будет возвращать указатель на объект вовнутрь lua в виде user_data. Операции над ним перекрыты с помощью т.н. метатаблиц. Луа скрипт выглядит так:

    local obj = trygetobject()
    local name = obj.Name
    obj.Name = "I was "..name.." some day, but i got NAME now!"
    for i=1,10 do
     obj.Size = obj.Size + 1
    end


             
     и он рулит объектом, описанным так:
     
     {$M+}
     TMyObj = class(TObject)
     private
       FName: string;
       FSize: Integer;
       procedure SetName(const Value: string);
       procedure SetSize(const Value: Integer);
     published
       property Size: Integer read FSize write SetSize;
       property Name: string read FName write SetName;

       constructor Create; virtual;
     end;
     {$M-}



    Если у кого-нибудь есть идеи, как разрулить вызов методов из lua кода, был бы счастлив их услышать. Да, измененный модуль (с примером использования) лежит тут:
    http://slil.ru/26429923
  • Servy © (12.12.08 14:31) [28]
    Забыл добавить, размер архива по ссылке 174 Кб.
  • VMcL © (12.12.08 14:38) [29]
    >>Городской Шаман   (12.12.08 13:58) [24]

    У каждого свои тараканы.
    Мои позволяют, единожды написав и протестировав код, возвращаться к нему только для добавления новой функциональности.
  • Городской Шаман (12.12.08 14:44) [30]

    > Palladin ©   (12.12.08 14:12) [26]
    >
    >
    > >  с читабельностью кода стремящейся к 0%.
    >
    > Хм, а как повредит единоразовое определение размера дин.
    >  массива на читабельность кода?


    Тем что в данном случае указатели на обработчики lua функций добавляются в динамике и для разных проблемных областей там может быть разное количество, а здесь нужно шаманить со статикой и пользователю компонента знать как он устроен внутри

    Кроме того если Delphi уж берёт на "магию компилятора" динамические массивы то и компилятор должен заботится  и о выделении памяти блоками под него.

    Во множестве реализаций std::vector выделяется память для динамического массива блоками определенной длинны и именно разработчик библиотеки берет на себя ответственность за распределение памяти внутри.

    А если компилятор при вызове Setlength(,+1) каждый раз перераспределяет память, то это кривой компилятор.
  • Servy © (12.12.08 14:56) [31]
    > А если компилятор при вызове Setlength(,+1) каждый раз перераспределяет
    > память, то это кривой компилятор.


    Компилятор ничего не перераспределяет, он всего-лишь вставляет туда вызов соответствующей функции менеджера памяти. А менеджер памяти, если я правильно помню, можно заменить. Вроде как, именно это делал FastMM, недавно мелькало обсуждение.

    В общем-то, спор ни о чем. Смысла оптимизировать при количестве добавляемых функций порядка 10-1000 по-видимому нет (добавление единократное, так как функции удаления из списка нет ^_^). С другой стороны, наличие такой оптимизации, тоже ничем не помешает (сложность кода повысится уж очень незначительно, говорить о нечитабельности или большой вероятности допустить там ошибку - преувеличение). Так что, пускай каждый и дальше делает, как ему нравится.
  • Городской Шаман (12.12.08 14:59) [32]

    > VMcL ©   (12.12.08 14:38) [29]
    >
    > >>Городской Шаман   (12.12.08 13:58) [24]
    >
    > У каждого свои тараканы.
    > Мои позволяют, единожды написав и протестировав код, возвращаться
    > к нему только для добавления новой функциональности.


    Тогда зачем нам Delphi давайте бухгалтерские программы писать сразу на assembler с использованием SSE и CUDA для повышения производительности.
  • Mystic © (12.12.08 15:37) [33]
    > Городской Шаман   (12.12.08 14:59) [32]

    Во всем есть здравый смысл. Давай рассмотрим конкретно эту ситуацию.

    1. Это библиотечный кот. Поэтому для данного кота вопрос оптимизации имеет большую роль в силу так называемого экспоненциального взрыва.

    2. Скорость работы алгоритма зависит от длины массива. Предположим достаточно типичную ситуацию, что нам надо добавить 10 000 методов. В самом печальном случае на это потребуется примерно столько же релокаций памяти плюс копирование. Опять, отбрасывая копейки, средний размер массива 40 000 * 0.5 байт = 20 000 байт, умножая на 10 000 релокаций получаем копирование 200 Mb. Конечно, менеджер памяти у Delphi хороший, и не всякая релокация будет связана с копированием. В .NET это может оказаться более фатальным. Если там не предусмотрено специальных оптимизаций, то может понадобится 10 000 выделений памяти, несколько запусков сборщика мусора, а также копирование всего заявленного объема.

    3. Если в данном случае использовать TStringList, код станет и короче, и понятнее. Но даже если и самому ввести дополнительную переменную и добавить условие, то это не никоим образом не сделает программу шаманской.
  • @!!ex © (12.12.08 15:40) [34]
    > [33] Mystic ©   (12.12.08 15:37)

    Lua скрипт с 10 000 глобальных методов сдохнет сам по себе, независимо от реаллока.
  • Mystic © (12.12.08 15:53) [35]
    А чего ему сдыхать? Никто не говорит о том, что все эти методы будут вызываться.
  • @!!ex © (12.12.08 16:04) [36]
    > [35] Mystic ©   (12.12.08 15:53)

    Обращение к функции идет через вызов метода lua_getglobal.
    Это перебор всех глобальных значений с сравнением строк.
    Для каждого вызова lua_getglobal в среднем нужно 5000 переборов.
    конечно там не используется явный перебор и явное сравнение строк, что позволяет чуть ускорить это дело. Но суть не меняется. Такое количество глобальных значений будет зверско тормозить.
    Тем более, что в глобальном пространстве хрянятся не только внешние функции, но и множество внутренних данных, которые работают через тот-же lua_getglobal. Так что это будет тормозить не только при вызове функций С из луа, но и при внутренних вызовах функций и переменных.

    Опять же пример оторван от жизни. 10 000 внешних функций... Зачем? ВСе взаимодействие обычно происходит
    через два десятка функций. Как правило при реализации стараются избежать частого вызова lua функций из кода и наоборот.

    Оптимизацию такого плана считаю глупой. Оптимизировать надо там, где тормозит. Или где, хотя бы теоретически, может тормозить. ЗДесь не может тормозить даже теоретически. Потому что при достижении большого количества методов возникнут тормоза в других точках, и значительно более серьезные.
  • Городской Шаман (12.12.08 16:15) [37]

    > Mystic ©   (12.12.08 15:37) [33]


    По поводу (2) тогда стоит стоит добавить отдельный метод AddMethodList(array of ...) который и будет добавлять уже сформированный массив у "клиентского кода" из 10000 методов.

    И вся проблема. Но второй метод если у будет использоваться, то только в 0.0001% случаев.
  • Городской Шаман (12.12.08 16:18) [38]

    > Mystic ©   (12.12.08 15:37) [33]


    С другой стороны - 10000 методов, да в WinApi поменьше функций будет.

    Если 10000 методов то нужно не lua, а Java + Rhino JavaScript + мейнфрейм за 10000000$.
  • Mystic © (12.12.08 16:42) [39]
    > Городской Шаман   (12.12.08 16:15) [37]

    Можно добавить и такой метод. Но если изначально размер списка методов установить равных 128, а при исчерпании лимита увеличивать размер ровно в два раза, то нам понадобится семь релокаций, при этом памяти будет скопировано ~ 100 кб, что всего в два с половиной раза больше первоначального размера списка. Разницы между твоим оптимизированным методом AddMethodList и добавлением методов в цикле никто и не заметит. Тем более, что этот массив, который передается в качестве аргумента, также надо будет сформировать. Очень может быть, что он читается из файла, и нам заранее неизвестно сколько в нем элементов. История повторяется :)

    > Городской Шаман   (12.12.08 16:18) [38]

    Конечно, если писать совсем не задумываясь об оптимизации, то никаких денег не хватит :) Вопрос еще и в том, что всякий раз, когда мне нужно добавить элемент к массиву, мне надо думать, к чему это приведет. Не проще ли забыть об этим потенциальных граблях и приучить себя писать дополнительные пару строк кода? А в случае TStringList и вовсе удалить пару строк. Ведь обходится большинство потенциальных проблем, а усилий никаких.
  • @!!ex © (12.12.08 16:51) [40]
    > [39] Mystic ©   (12.12.08 16:42)

    Приведите код с TStringList, сравним читабельность.
    Как уже говорилось выше - выделение блоками применятся(лично мной) там, где это актуально. Почему ЗДЕСЬ не актауально, уже объяснил.
  • Palladin © (12.12.08 16:58) [41]

    > Приведите код с TStringList, сравним читабельность.

    добавление sl.Add(Name,TObject(Address));

    обращение

    procedure TLUAParser.LuaUpdateMethods;
    var
    i:integer;
    begin
    for I := 0 to Length(FExternMethods) - 1 do
      LuaAddMethod(sl[i],lua_CFunction(sl.Objects[i]));
    end;

    ку ?
  • Sapersky (12.12.08 16:59) [42]
    Автор, вроде как, пишет на BDS, а там (начиная с 2006-го) проблема решена в корне - сам менеджер памяти (FastMM) перевыделяет память блоками, на манер TList. О чём, собственно, уже писали в [31].
    Хотя лично я, чтобы не зависеть от версий и менеджеров, предпочитаю самописную "шаманскую" функцию перевыделения.
  • Mystic © (12.12.08 17:27) [43]
    > Sapersky   (12.12.08 16:59) [42]

    Смотреть надо, какой минимальный размер блока, какой между ними шаг (постоянный/экспоненциальный), алгоритм перераспределения...
  • Городской Шаман (12.12.08 17:37) [44]

    > Mystic ©   (12.12.08 16:42) [39]


    А не проще ли поставить нормальный менеджер памяти, который об этом позаботится автоматически?

    FastMM - нет проблем! :)
  • Городской Шаман (12.12.08 17:41) [45]

    > Mystic ©   (12.12.08 17:27) [43]
    >
    > > Sapersky   (12.12.08 16:59) [42]
    >
    > Смотреть надо, какой минимальный размер блока, какой между
    > ними шаг (постоянный/экспоненциальный), алгоритм перераспределения.


    Угу написать имитационную программу в matlab и на ядро системы затратить 15 лет (Minix3), когда Linux, не говоря уже о тупой кривой и глюкавой винде (вспомним работоспособность Win95) вовсю господствуют на рынке.
  • clickmaker © (12.12.08 17:42) [46]
    > вспомним работоспособность Win95

    лучше 3.1
  • Sapersky (12.12.08 17:42) [47]
    Смотреть надо, какой минимальный размер блока, какой между ними шаг (постоянный/экспоненциальный), алгоритм перераспределения...

    Алгоритм перевыделения в общем похож на TList.Grow. Для маленьких блоков в 2 раза (+ ещё какой-то довесок), для средних и больших - на 25%. Правда, какой конкретно размер у этих больших-маленьких - не смотрел. Но наверное уж подобран оптимальный.

    {This pointer is being reallocated to a larger block and therefore it is
         logical to assume that it may be enlarged again. Since reallocations are
         expensive, there is a minimum upsize percentage to avoid unnecessary
         future move operations.}
        {Must grow with at least 100% + x bytes}
        LNewAllocSize := LOldAvailableSize * 2 + SmallBlockUpsizeAdder;

    Для "средних" блоков:

          {Couldn't upsize in place. Grab a new block and move the data across:
           If we have to reallocate and move medium blocks, we grow by at
           least 25%}
          LMinimumUpsize := LOldAvailableSize + (LOldAvailableSize shr 2);

    Для "больших" тоже 25%.

    ( http://pda.delphimaster.net/?id=1226920476&n=0 )
  • Городской Шаман (12.12.08 17:44) [48]

    > Sapersky   (12.12.08 16:59) [42]
    >
    > Автор, вроде как, пишет на BDS, а там (начиная с 2006-го)


    Так ничего не мешает даже к Delphi5 подключить FastMM опционально. Или на Google ссылку не нашли?

    Извините, но когда мне понадобится оптимизация, а начну часть кода писать на assembler, там будет гораздо более эффективное распределение.
  • Городской Шаман (12.12.08 17:49) [49]

    > Sapersky   (12.12.08 17:42) [47]


    У динамических массивов есть такая замечательная особенность как управление их жизнью со стороны компилятора, по типу интерфейсов в Delphi. Во многих случаях возможность не заниматься освобождением памяти облегчает жизнь и избавляет от глюков.

    Поэтому и Java и C# пользуются популярностью, проигрывая нативным языкам во всем остальном. Повисший сервер это очень плохо, а на C++ или Delphi он до такой стабильности вылизывается годами.
  • Mystic © (12.12.08 18:20) [50]
    > Sapersky   (12.12.08 17:42) [47]

    Да, я уже посмотрел. Все равно в мозгу мало ячеек памяти чтобы помнить что, где и как реализовано. Что опасно, а что нет. Привычка со старых времен.
  • @!!ex © (14.12.08 17:45) [51]
    Охрененный такой косячек в коде...
    lua_pop(FLUAState, 1);


    должно быть
    lua_pop(FLUAState, -1);


    Ну и соответственно где другие цифры, все равно - должен быть.
  • @!!ex © (14.12.08 18:00) [52]
    Еще косяк. pcall второй аргумент - это количество результатов. Он в моем коде всегда 0. Это не правильно.
    вернее для процедур это правильно. А вот функции должны указывать количество результатов, иначе нифига работать не будет.
  • Kostafey © (18.12.08 01:45) [53]
    > Сделал классовую обертку(может кому понадобится?).

    Э-э-э вроде ж ketmar делал?
    Хотя, я чессно говоря не особо вник.
 
Конференция "Прочее" » Оценил простоту и мощь Lua
Есть новые Нет новых   [134449   +17][b:0][p:0.003]