Question

In my application I have many sets defined:

eBlockTypes = (btNone,btUndefined,btStone, btYellowFlower, btWoodBrown...);

sMinerals = set of eBlockTypes; 

var
  mineralsRare: sMinerals;
  mineralsPlants: sMinerals;
  mineralsAll: sMinerals;
  mineralsDeep: sMinerals;
  mineralsWalkable: sMinerals;
  mineralsDiggable: sMinerals; 

then I have an object that has 'sMinerals' as one of it's fields. Is it possible to read set's 'name' when loading object properties from file?

edit: more details. Let's say that object definition in file looks like that:

[item]
Computer
[requires]
3 Circuit board
1 Medium CPU
3 Plastic
[placement]
mineralsWalkable

so I can parse the file and read all the properties besides the set 'mineralsWalkable'. I know that I could compare that string with some TStrings holding all the sets names but the quiestion is: is it possible get that set by converting the string to variable somehow?

Was it helpful?

Solution

What you can read from a file depends on what's in the file. If you wrote the names of your variables into the file, then you should be able to read them, too. If not, then you can't. Variables names are not inherently written to files when you wrote data, though.

Identify the technique used to write the names into your file. To read them, simply perform the inverse operation. If you wrote the data delimited in some way, then read until you encounter a delimiter. If you preceded the name with its character length, then read the length, and then read that many characters. If you didn't write the names with a technique that's invertible, then you'll have to change how you write your data before proceed to reading it.

Your question asked whether it was possible to read the names, and the answer is yes. You've since asked another question, which is whether it's possible to "convert" the name read from the file into the actual variable with the corresponding name. The answer to that is no.

Ordinary variables do not have RTTI; Delphi does not maintain the names of all the variables in a program. Once the compiler finishes its job, the names cease to exist within the program.

The easiest way to get the variable is to set up a mapping from string to set value. Read the name from the file, look up the name in a data structure, and use the corresponding value. A TDictionary<string, sMinerals> would be perfect for that. Just populate the data structure as your program starts up.

OTHER TIPS

Pretty Simple... You need to save your variable mineralsWalkable to a string representation of your eBlockTypes. You'll use GetEnumName to do that. You then need to convert your string representation of your eBlockTypes to an actual eBlockType and then add that to your mineralsWalkable. You'll use GetEnumValue to do that.

The following example shows taking the string representation...placing it in a set...and then taking the set...and moving it back to a string...

object Form54: TForm54
  Left = 0
  Top = 0
  Caption = 'Form54'
  ClientHeight = 290
  ClientWidth = 554
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 56
    Top = 160
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Edit1: TEdit
    Left = 56
    Top = 64
    Width = 265
    Height = 21
    TabOrder = 1
    Text = 'Edit1'
  end
  object cbType1: TCheckBox
    Left = 248
    Top = 91
    Width = 97
    Height = 17
    Caption = 'Type1'
    TabOrder = 2
  end
  object cbType2: TCheckBox
    Tag = 1
    Left = 248
    Top = 114
    Width = 97
    Height = 17
    Caption = 'Type2'
    TabOrder = 3
  end
  object cbType3: TCheckBox
    Tag = 2
    Left = 248
    Top = 137
    Width = 97
    Height = 17
    Caption = 'Type3'
    TabOrder = 4
  end
end

unit Unit54;
{Note the code assumes cbType1.Tag = 0, cbType2.Tag = 1, and cbType3.Tag = 2}
interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, TypInfo, IniFiles;

type
  TMyType = (mtOne, mtTwo, mtThree);
  TMyTypes=  set of TMyType;

  TForm54 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    cbType1: TCheckBox;
    cbType2: TCheckBox;
    cbType3: TCheckBox;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    private
    function getTypes1: TMyTypes;
    procedure setMyTypes1(const Value: TMyTypes);
    { Private declarations }

  public
    { Public declarations }
    property Types1: TMyTypes read getTypes1 write setMyTypes1;

    procedure SaveMyTypes(aVariableName: string; aMyTypes: TMyTypes);
    function ReadMyTypes(aVariableName: string): TMyTypes;
  end;

var
  Form54: TForm54;

implementation

{$R *.dfm}

procedure TForm54.Button1Click(Sender: TObject);
var
  a_MyTypes: TMyTypes;
  a_Str: string;
  a_Index: integer;
begin
  a_MyTypes := [];
  a_Str := '';
  Include(a_MyTypes,  TMyType(GetEnumValue(TypeInfo(TMyType), 'mtOne')));
  Include(a_MyTypes,  TMyType(GetEnumValue(TypeInfo(TMyType), 'mtTwo')));
//purpoesly have mtThree3 instead of mtThree
  Include(a_MyTypes,  TMyType(GetEnumValue(TypeInfo(TMyType), 'mtThree3')));
  for a_Index := Ord(Low(TMyType)) to Ord(High(TMyType)) do
    if TMyType(a_Index) in a_MyTypes then
      if a_Str = '' then
        a_Str := GetEnumName(TypeInfo(TMyType), a_Index)
      else
        a_Str := a_Str + ',' + GetEnumName(TypeInfo(TMyType), a_Index);
//should be mtOne, mtTwo
  Edit1.Text := a_Str;
end;

procedure TForm54.FormCreate(Sender: TObject);
begin
  Types1 := ReadMyTypes('Types1');
end;

procedure TForm54.FormDestroy(Sender: TObject);
begin
  SaveMyTypes('Types1', Types1);
end;

function TForm54.getTypes1: TMyTypes;
var
  a_Index: integer;
begin
  Result := [];
  for a_Index := 0 to Self.ComponentCount - 1 do
    if Self.Components[a_Index] is TCheckBox and (TCheckBox(Self.Components[a_Index]).Checked) then
      Include(Result, TMyType(Self.Components[a_Index].Tag));
end;

function TForm54.ReadMyTypes(aVariableName: string): TMyTypes;
var
  a_Ini: TIniFile;
  a_Var: string;
  a_List: TStrings;
  a_Index: integer;
begin
  Result := [];
  a_Ini := nil;
  a_List := nil;
  a_Ini := TIniFile.Create('MyType.ini');
  a_List := TStringList.Create;
  try
    a_Var := a_Ini.ReadString('Sets', aVariableName, '');
    a_List.Delimiter := ',';
    a_List.DelimitedText := a_Var;
    for a_Index := 0 to a_List.Count - 1 do
    begin
      Include(Result, TMyType(GetEnumValue(TypeInfo(TMyType), a_List[a_Index])));
    end;
  finally
    a_Ini.Free;
    a_List.Free;
  end;
end;

procedure TForm54.SaveMyTypes(aVariableName: string; aMyTypes: TMyTypes);
var
  a_Ini: TIniFile;
  a_Index: integer;
  a_Var: string;
begin
  a_Var := '';
  a_Ini := TIniFile.Create('MyType.ini');
  try
    for a_Index := Ord(Low(TMyType)) to Ord(High(TMyType)) do
      if TMyType(a_Index) in aMyTypes then
        if a_Var = '' then
          a_Var := GetEnumName(TypeInfo(TMyType), a_Index)
        else
          a_Var := a_Var + ',' + GetEnumName(TypeInfo(TMyType), a_Index);
    a_Ini.WriteString('Sets', aVariablename, a_Var);
  finally
    a_Ini.Free;
  end;
end;

procedure TForm54.setMyTypes1(const Value: TMyTypes);
var
  a_Index: integer;
begin
  for a_Index := 0 to Self.ComponentCount - 1 do
    if Self.Components[a_Index] is TCheckBox then
      TCheckBox(Self.Components[a_Index]).Checked := TMyType(TCheckBox(Self.Components[a_Index]).Tag) in Value;

end;

end.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top