问题
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:
Using
record
to enlist entrypoints would require passing TWO pointers: the record itself andTypeInfo
. Which is redundant and can potentially get incoherent.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