Вряд ли кто это поймёт, но записываю, скорее "для себя", чтобы после краткосрочного отпуска вспомнить "мелькнувшую мысль".
+Виктор Морозов Витя, напомни, если будет не трудно - я тебе "на пальцах расскажу". Это к сегодняшнему разговору об "операторах" и параметрах "слева и справа".
Чтобы определить например оператор ".Height" для контрола делаем следующее:
Эту часть, без "кодогенерации" сделать МОЖНО, но - "муторно". А вот далее пойдут - вполне себе "проектные классы".
Тогда возможен код на скрипте:
Теперь как выглядит TOperator.RegisterProp:
Теперь как выглядит TtfwClassOperatorCompiler.Register:
Тут фишка какая? А такая, что TtfwClassOperatorCompiler НАСЛЕДУЕТСЯ от TtfwWordWorkerRunner, а посему - все "сложности компиляции" берёт на себя ИМЕННО - TtfwWordWorkerRunner.
Теперь как выглядит TtfwClassOperatorCompiled:
Заметка "на полях":
Теперь положим мы хотим определить свойство Height для TControl и TvgControl.
Что делать?
А тут мы делаем bundle.
TControlHeightImpl и TvgControlHeightImpl - в общем - похожи.
Ну "с точностью до запятых".
А вот TtfwClassOperatorCompiler и TtfwClassOperatorCompiled - видоизменяются достаточно СИЛЬНО.
И вообще говоря - "приобретают черты" Dependency Injection.
Посмотрим для начала на TtfwClassOperatorCompiler.Register:
Изменим его примерно так:
Теперь как выглядит TtfwClassOperatorCompiled?
Пусть у нас есть "ассоциативный массив" вида:
Далее будем обобщать эту схему на переменное число параметров и неклассовые GetSelf.
С "новым" RTTI всё "относительно проще, но всё равно без TtfwClassOperatorCompiler и TtfwClassOperatorCompiled не обойтись.
Можно кстати начать с более простого:
Или:
+Виктор Морозов Витя, напомни, если будет не трудно - я тебе "на пальцах расскажу". Это к сегодняшнему разговору об "операторах" и параметрах "слева и справа".
Чтобы определить например оператор ".Height" для контрола делаем следующее:
type TControlHeightImpl = class ... end;//TControlHeightImpl class function TControlHeightImpl.GetValueSemiGenerated(const aCtx: TtfwContext; aSelf: TControl): Integer; begin // Start UC Result := aSelf.Height; // End UC end; class procedure TControlHeightImpl.SetValueSemiGenerated(const aCtx: TtfwContext; aSelf: TControl; aValue: Integer); begin // Start UC aSelf.Height := aValue; // End UC end; class function TControlHeightImpl.GetValueGenerated(const aCtx: TtfwContext; aSelf: TObject): TtfwValue; begin Result := TtfwValue_C(GetValueSemiGenerated(aCtx, TControl(aSelf))); end; class procedure TControlHeightImpl.SetValueGenerated(const aCtx: TtfwContext; aSelf: TObject; const aValue: TtfwValue); begin SetValueSemiGenerated(aCtx, TControl(aSelf), aValue.AsInteger); end; initialization TOperator.RegisterProp('Height', TControl, TControlHeightImpl.GetValueGenerated, TControlHeightImpl.SetValueGenerated);
Эту часть, без "кодогенерации" сделать МОЖНО, но - "муторно". А вот далее пойдут - вполне себе "проектные классы".
Тогда возможен код на скрипте:
OBJECT VAR X X := GetControlFromSomewhere X .Height . X .Height := SomeValue
Теперь как выглядит TOperator.RegisterProp:
type TtfwObjectPropGetter = function (const aCtx: TtfwContext; aSelf: TObject): TtfwValue of object; TtfwObjectPropSetter = procedure (const aCtx: TtfwContext; aSelf: TObject; const aValue: TtfwValue) of object; ... class procedure TOperator.RegisterProp(const aPropName: AnsiString; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); var l_Key : Il3CString; begin l_Key := TtfwStringFactory.C('.' + aPropName); TtfwClassOperatorCompiler.Register(Self, l_Key, aClass, aGetter, aSetter); end;
Теперь как выглядит TtfwClassOperatorCompiler.Register:
type TtfwClassOperatorCompiler = class(TtfwWordWorkerRunner) ... end;//TtfwClassOperatorCompiler class procedure TtfwClassOperatorCompiler.Register(aProducer: TtfwWord; const aKey: Il3CString; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); var l_Inst : TtfwClassOperatorCompiler; begin l_Inst := Self.Create(aProducer, aKey, aClass, aGetter, aSetter); try l_Inst.RegisterInAxiomatics; finally FreeAndNil(l_Inst); end;//try..finally end; constructor TtfwClassOperatorCompiler.Create(aProducer: TtfwWord; const aKey: Il3CString; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); var l_Compiled : TtfwClassOperatorCompiled; begin inherited Create(aProducer, aKey); // - с aProducer и aKey - пусть разбирается предок l_Compiled := TtfwClassOperatorCompiled.Create(aKey, Self, aClass, aGetter, aSetter); try l_Compiled.SetRefTo(f_Compiled); finally FreeAndNil(l_Compiled); end;//try..finally end;
Тут фишка какая? А такая, что TtfwClassOperatorCompiler НАСЛЕДУЕТСЯ от TtfwWordWorkerRunner, а посему - все "сложности компиляции" берёт на себя ИМЕННО - TtfwWordWorkerRunner.
Теперь как выглядит TtfwClassOperatorCompiled:
constructor TtfwClassOperatorCompiled.Create(const aKey: Il3CString; aCompiler: TtfwClassOperatorCompiler; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); begin inherited Create(aKey); // - с aKey - пусть разбирается предок f_Class := aClass; f_Getter := aGetter; f_Setter := aSetter; f_Compiler := aCompiler; Self.DefineLeftWordRefParam('Self'); // - определяем один ссылочный параметр слева, чисто для документации и диагностики end; function TtfwClassOperatorCompiled.GetSelf(const aCtx: TtfwContext): TObject; begin Result := f_Compiler.LeftWordRefValues.First.GetValue(aCtx).AsObject; // - получаем значение параметра у того парня, который нас компилировал //Result := LeftWordRefParams.First.GetValue(aCtx).AsObject; CompilerAssert(Result <> nil, 'Подан пустой объект', aCtx); CompilerAssert(Result.InheritsFrom(f_Class), 'Подан объект класса ' + Result.ClassName + ', а должен быть ' + f_Class.ClassName, aCtx); end; function TtfwClassOperatorCompiled.GetValue(const aCtx: TtfwContext): TtfwValue; override; begin Result := f_Getter(aCtx, Self.GetSelf(aCtx)); end; procedure TtfwClassOperatorCompiled.SetValue(const aCtx: TtfwContext; const aValue: TtfwValue); override; begin f_Setter(aCtx, Self.GetSelf(aCtx), aValue); end; procedure TtfwClassOperatorCompiled.DoDoIt(const aCtx: TtfwContext); begin if aCtx.rNeedValue then aCtx.Push(Self.GetValue(aCtx)) else CompilerAssert(false, 'Что-то пошло не так', aCtx); end;
Заметка "на полях":
- "определяем один ссылочный параметр слева, чисто для документации и диагностики" - ХРЕНА! НЕ ТОЛЬКО для диагностики, но и для ТОГО, чтобы ВЕРНО скомпилировать "ссылочный параметр слева".
Теперь положим мы хотим определить свойство Height для TControl и TvgControl.
Что делать?
А тут мы делаем bundle.
Unit ControlsPublishing; ... initialization TOperator.RegisterProp('Height', TControl, TControlHeightImpl.GetValueGenerated, TControlHeightImpl.SetValueGenerated); ... Unit vgControlsPublishing; ... initialization TOperator.RegisterProp('Height', TvgControl, TvgControlHeightImpl.GetValueGenerated, TvgControlHeightImpl.SetValueGenerated);
TControlHeightImpl и TvgControlHeightImpl - в общем - похожи.
Ну "с точностью до запятых".
А вот TtfwClassOperatorCompiler и TtfwClassOperatorCompiled - видоизменяются достаточно СИЛЬНО.
И вообще говоря - "приобретают черты" Dependency Injection.
Посмотрим для начала на TtfwClassOperatorCompiler.Register:
Изменим его примерно так:
class procedure TtfwClassOperatorCompiler.Register(aProducer: TtfwWord; const aKey: Il3CString; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); var l_Inst : TtfwClassOperatorCompiler; begin if Axiomatics.Registered(aKey, l_Inst) then l_Inst.AddAnotherClass(aClass, aGetter, aSetter) else begin l_Inst := Self.Create(aProducer, aKey, aClass, aGetter, aSetter); try l_Inst.RegisterInAxiomatics; finally FreeAndNil(l_Inst); end;//try..finally end;//Axiomatics.Registered(aKey, l_Inst) end; procedure TtfwClassOperatorCompiler.AddAnotherClass(aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); begin f_Compiled.AddAnotherClass(aClass, aGetter, aSetter); end;
Теперь как выглядит TtfwClassOperatorCompiled?
Пусть у нас есть "ассоциативный массив" вида:
type TtfwClassOperatorCompiledInfo = record rClass : TClass; rGetter : TtfwObjectPropGetter; rSetter : TtfwObjectPropSetter; constructor Create(aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); end;//TtfwClassOperatorCompiledInfo TtfwClassOperatorCompiledInfoList = class(TList<TtfwClassOperatorCompiledInfo>) public procedure AddIfNotHasClass(const aClass: TtfwClassOperatorCompiledInfo); end;//TtfwClassOperatorCompiledInfo procedure TtfwClassOperatorCompiledInfoList.AddIfNotHasClass(const aClass: TtfwClassOperatorCompiledInfo): Boolean; var l_Info : TtfwClassOperatorCompiledInfo; begin for l_Info in Self do if (aClass.rClass = l_Info.rClass) then Exit; Self.Add(aClass); end;Тогда класс TtfwClassOperatorCompiled будет выглядеть так:
type TtfwClassOperatorCompiled = ... private f_List : TtfwClassOperatorCompiledInfoList; end;//TtfwClassOperatorCompiled constructor TtfwClassOperatorCompiled.Create(const aKey: Il3CString; aCompiler: TtfwClassOperatorCompiler; aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); begin inherited Create(aKey); // - с aKey - пусть разбирается предок f_List := TtfwClassOperatorCompiledInfoList.Create; f_List.Add(TtfwClassOperatorCompiledInfo.Create(aClass, aGetter, aSetter)); f_Compiler := aCompiler; Self.DefineLeftWordRefParam('Self'); // - определяем один ссылочный параметр слева, чисто для документации и диагностики end; procedure TtfwClassOperatorCompiled.AddAnotherClass(aClass: TClass; aGetter: TtfwObjectPropGetter; aSetter: TtfwObjectPropSetter); begin f_List.AddIfNotHasClass(TtfwClassOperatorCompiledInfo.Create(aClass, aGetter, aSetter)); end; type TInvokeOnSelf = reference to procedure (anObj: TObject; const anInfo: TtfwClassOperatorCompiledInfo); procedure TtfwClassOperatorCompiled.InvokeOnSelf(const aCtx: TtfwContext; aWhatToInvoke: TInvokeOnSelf); var l_Info : TtfwClassOperatorCompiledInfo; l_Obj : TObject; begin l_Obj := f_Compiler.LeftWordRefValues.First.GetValue(aCtx).AsObject; // - получаем значение параметра у того парня, который нас компилировал CompilerAssert(Result <> nil, 'Подан пустой объект', aCtx); for l_Info in f_List downto do if (l_Obj.InheritsFrom(l_Info.rClass)) then begin aWhatToInvoke(l_Obj, l_Info); Exit; end; CompilerAssert('Подан объект класса ' + l_Obj.ClassName + ', который непонятно как обрабатывать', aCtx); end; function TtfwClassOperatorCompiled.GetValue(const l_Obj TtfwContext): TtfwValue; override; begin InvokeOnSelf(aCtx, procedure (anObj: TObject; const anInfo: TtfwClassOperatorCompiledInfo) begin Result := anInfo.rGetter(aCtx, anObj); end; ); end; procedure TtfwClassOperatorCompiled.SetValue(const aCtx: TtfwContext; const aValue: TtfwValue); override; begin InvokeOnSelf(aCtx, procedure (anObj: TObject; const anInfo: TtfwClassOperatorCompiledInfo) begin anInfo.rSetter(aCtx, anObj, aValue); end; ); end; procedure TtfwClassOperatorCompiled.DoDoIt(const aCtx: TtfwContext); begin if aCtx.rNeedValue then aCtx.Push(Self.GetValue(aCtx)) else CompilerAssert(false, 'Что-то пошло не так', aCtx); end;
Далее будем обобщать эту схему на переменное число параметров и неклассовые GetSelf.
С "новым" RTTI всё "относительно проще, но всё равно без TtfwClassOperatorCompiler и TtfwClassOperatorCompiled не обойтись.
Можно кстати начать с более простого:
type TGetAddr = class end;//TGetAddr procedure TGetAddr.DoIt(const aCtx: TtfwContext; aRightParam: TtfwWord); begin aCtx.rEngine.PushObj(aRightParam); end; initialization TOperator.RegisterWordRefAtRight('@', TGetAddr.DoIt);
Или:
type TWhile = class end;//TWhile procedure TWhile.DoIt(const aCtx: TtfwContext; aRightParam1: TtfwWord; aRightParam2: TtfwWord); begin while aRightParam1.DoAndTrue(aCtx) do // - выполняем код слова aRightParam1 и проверяем его результат на ИСТИННОСТЬ aRightParam2.DoOnly(aCtx); // - выполняем код слова aRightParam2 end; initialization TOperator.RegisterWordRefAtRight2('WHILE', TWhile.DoIt);
Комментариев нет:
Отправить комментарий