Can one get/set class-level members using Enhanced RTTI in Delphi?

ぐ巨炮叔叔 提交于 2019-12-24 13:10:59

问题


Preface, it seems i failed to say it clear. I want to enumerate, read and set, all the class var or class properties of a given TClass variable. There is no problem in finding TClass - it is passed. There is no problem in filtering by presence of given attribute. There problem is that RTTI just misses the way to enumerate class fields rather than instance fields.


I wanted to make a declarative DLL Loader. Since the DLLs are process-global in windows (you cannot load the same DLL twice) it corresponds to class-level fields or properties rather than to instant-level ones.

So i thought something like LoadDLL(filename:string; funtions: TClass) and use attributes to specify which entry points to fetch.

Then i run into the wall: while there is TRttiClassRefType in Delphi - one just cannot get it by given instance or whatever: TRTTIContext.GetType(Class) returns context for instances, rather than for metatypes.

Enumerating all the metatypes until the needed one got by name is... ugly. And fragile. And slow. Though it seems to be the only real code snippet containing TRttiClassRefType that i can find.

So - is there a way i can get and set class-level variables or properties, finding them, by some custom attribute attached ?

The following test program only finds IV and IP, and skips CV and CP...

program Project19;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, RTTI;

type
  DLL_Info = class (TCustomAttribute) end;

  {$M+} {$RTTI EXPLICIT FIELDS ([vcPublic]) PROPERTIES ([vcPublic])}
  DLL_Functions = class
     public
       [DLL_Info] var IV: pointer;  // type is just stub
       [DLL_Info] class var CV: pointer;
       [DLL_Info] property IP: pointer read IV;
       [DLL_Info] class property CP: pointer read CV;
  end;
  {$M-}

var df: DLL_Functions;

procedure SetDLLFunctions;
var
  LContext: TRttiContext;
  LType: TRttiType;
  LVar:  TRttiField;
  LProp: TRttiProperty;
  LAttr: TCustomAttribute;

  DLL_fn_Entry, DLL_fn_Optional: boolean;
  DLL_fn_Name: string;
  fn_ptr: Pointer;
begin

  LContext := TRttiContext.Create;
  try
    LType := LContext.GetType(DLL_Functions);
    for LVar in LType.GetFields do begin
        Writeln(Lvar.Name);
    end;
    for LProp in LType.GetProperties do begin
        Writeln(LProp.Name);
    end;
  finally
    LContext.Free;
  end;
end;

begin
  try
   df := DLL_Functions.Create;

   df.IV := @SetDLLFunctions; // imitating DLL quering by GetProcAddress
   df.CV := @SetDLLFunctions;

   SetDLLFunctions;

   df.Destroy;

   ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Some possible workarounds:

  1. Using record to enlist entrypoints would require passing TWO pointers: the record itself and TypeInfo. Which is redundant and can potentially get incoherent.

  2. Creating class instances just to hold static pointers would be one extra level of indirection when calling, and would be a redundant entity to keep track of.

来源:https://stackoverflow.com/questions/19495258/can-one-get-set-class-level-members-using-enhanced-rtti-in-delphi

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!