unit TestMethodCallFromRecordParams; interface // We need RTTI here! {$M+} uses System.Rtti, System.SysUtils, System.TypInfo, System.Classes, Myc.Data.Records; type InvokeAttribute = class(TCustomAttribute); FactoryAttribute = class(TCustomAttribute); TMyProc = reference to function(const [ref] Value: TValue): TResult; TFactoryProc = reference to function(const [ref] Params: TParams): TMyProc; TMyWorker = record type TParams = record Log: Int64; text: String; end; TValue = record xyz: Double; end; TResult = record val: Int64; desc: String; end; [Factory] class function CreateFactory: TFactoryProc; static; end; procedure Test1(const Log: TStrings); implementation class function TMyWorker.CreateFactory: TFactoryProc; begin Result := function(const [ref] Params: TParams): TMyProc begin var Log := TStrings(Params.Log); var text := Params.text; Result := function(const [ref] Value: TValue): TResult begin // Use Format to avoid locale issues with float conversion Log.Add(Format('Val=%f (...%s)', [Value.xyz, text])); Result.desc := 'done'; end; end; end; type TInvokeProc = reference to procedure(const Value, Res: TDataRecord); function CreateInvoker(Ctx: TRttiContext; CallFunc: TValue): TInvokeProc; begin var rttiMyProc := Ctx.GetType(CallFunc.TypeInfo); var workerInvokeMethod := rttiMyProc.GetMethod('Invoke'); var workerParamTypeInfo := workerInvokeMethod.GetParameters[0].ParamType.Handle; var args: array[0..0] of TValue; Result := procedure(const Value, Res: TDataRecord) begin TValue.MakeWithoutCopy(Value.RawData, workerParamTypeInfo, args[0], true); workerInvokeMethod.Invoke(CallFunc, args).ExtractRawData(Res.RawData); end; end; procedure Test1(const Log: TStrings); begin var params := TDataRecord.FromRecord; params.SetValue('Log', Int64(Log)); params.SetValue('text', 'The quick brown fox jumps...'); var workerValueParam := TDataRecord.FromRecord; workerValueParam.SetValue('xyz', 3.14159); var finalResultRec := TDataRecord.FromRecord; var invoke: TInvokeProc; //////////// var Ctx := TRttiContext.Create; var rttiType := Ctx.GetType(TypeInfo(TMyWorker)); // Find and invoke the method marked with [Factory] attribute. for var method in rttiType.GetMethods do begin if method.HasAttribute then begin // First invocation returns the factory procedure itself (an interface). var createFuncValue := method.Invoke(TValue.Empty, []); // Anonymous methods are interfaces, so we get the TRttiInterfaceType. var rttiCreateFunc := Ctx.GetType(createFuncValue.TypeInfo) as TRttiInterfaceType; // The actual code is in the 'Invoke' method of that interface. var factoryInvokeMethod := rttiCreateFunc.GetMethod('Invoke'); // Prepare the argument for the factory proc: a pointer to 'params' var factoryArg: array[0..0] of TValue; var factoryParamTypeInfo := factoryInvokeMethod.GetParameters[0].ParamType.Handle; TValue.Make(params.RawData, factoryParamTypeInfo, factoryArg[0]); var myProcValue := factoryInvokeMethod.Invoke(createFuncValue, factoryArg); invoke := CreateInvoker(Ctx, myProcValue); break; end; end; ////////// Assert(Assigned(invoke)); invoke(workerValueParam, finalResultRec); var desc := finalResultRec.GetValue('desc'); Log.Add('Final result description: ' + desc); Assert(desc = 'done'); end; end.