1
votes

Someone may already asked this question, but havent found it, so here it goes the problem:

I want to parse a component's tkSet properties (Panel1 in our case), but I dont know how to do that properly. I was able to find the base enumerated type of the set using rContext.FindType(), but I'm almost sure there are some more elegant/simple way to do that. From that point I'm completly lost. I should go thru on that enum type's values and check each value against the compoenent's present property value.

procedure TForm12.GetProperties2;
var
  rContext: TRttiContext;
  rType: TRttiType;
  rProp: TRttiProperty;
begin
  rType := rContext.GetType(Panel1.ClassType);

  for rProp in rType.GetProperties do
  begin
    if (rProp.Visibility in [mvPublished]) and (rProp.PropertyType.TypeKind in [tkSet]) and (rProp.Name = 'Anchors') then
    begin
      Memo1.Lines.Add('Name: ' + rProp.Name);
      Memo1.Lines.Add('PropertyType: ' + rProp.PropertyType.ToString);
      Memo1.Lines.Add('Value: ' + rProp.GetValue(Panel1).ToString);
      Memo1.Lines.Add('QualifiedName: ' + rProp.PropertyType.QualifiedName);
      Memo1.Lines.Add('ElementType: ' + rContext.FindType(rProp.PropertyType.QualifiedName).AsSet.ElementType.ToString);
      // here comes the desired results
      Memo1.Lines.Add('Possible values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add(' 2 > akRight');
      Memo1.Lines.Add(' 3 > akBottom');
      Memo1.Lines.Add('Present values:');
      Memo1.Lines.Add(' 0 > akLeft');
      Memo1.Lines.Add(' 1 > akTop');
      Memo1.Lines.Add('');
    end;
  end;
end;

Another possible problem is the set properties without base enum types, for example if you have a look on TPanel.StyleElements property you can see the TStyleElements' declaration is the following:

TStyleElements = set of (seFont, seClient, seBorder);

In this case the ElementType not working.

So the question is how can I parse the tkSet type properties to get the desired results using RTTI context?

1
Why does it have to be without the TypInfo unit?GolezTrol
because I wish to learn the RTTI functionalities nowtcxbalage
It's extended RTTI. I don't think the new RTTI is a full replacement. It uses at least PTypeInfo which is still in the unit TypInfo.GolezTrol
I see. So the TypInfo must be involved to solve this problem?tcxbalage
@tcxbalage: It is very unlikely you‘ll be able to achieve anything useful with RTTI without the TypInfo unit.Rudy Velthuis

1 Answers

3
votes

This is pretty easy using the basic TypInfo.

procedure PrintSet(const v: TValue); // v contains a value from a set type
var
  enumType: PTypeInfo;
  enumData: PTypeData;
  buffer: set of Byte; // biggest possible set type
  i: Integer;
begin
  buffer := [];
  v.ExtractRawData(@buffer);
  enumType := v.TypeInfo.TypeData.CompType^;
  enumData := enumType.TypeData;
  for i := enumData.MinValue to enumData.MaxValue do
    Writeln(GetEnumName(enumType, i) + ' = ' + (i in buffer).ToString(TUseBoolStrs.True));
end;

A set of Byte is the biggest set type possible so we can use this as buffer where everything will fit in and then use the TValue.ExtractRawData method to write as much data into it as the actual set type has. Everything else was zeroed by setting it to empty before.

Then we can use the type data of the enum type to get the min and max values. As non contiguous enum types don't have typeinfo we don't need to take care of that and in fact only handle those that are binary compatible with classic bitmasks.