I will answer this question myself, but feel free to provide your answers if you are faster than me or if you don't like my solution. I just came up with this idea and would like to have some opinions on that.
Goal: a configuration class that is readable (like an INI-file) but without having to write (and adapt after a new configuration item has been added) the load and save methods.
I want to create a class like
TMyConfiguration = class (TConfiguration)
...
property ShowFlags : Boolean read FShowFlags write FShowFlags;
property NumFlags : Integer read FNumFlags write FNumFlags;
end;
Calling TMyConfiguration.Save (inherited from TConfiguration) should create a file like
[Options]
ShowFlags=1
NumFlags=42
Question: What is the best way to do this?
This is my proposed solution.
I have a base class
TConfiguration = class
protected
type
TCustomSaveMethod = function (Self : TObject; P : Pointer) : String;
TCustomLoadMethod = procedure (Self : TObject; const Str : String);
public
procedure Save (const FileName : String);
procedure Load (const FileName : String);
end;
The Load methods look like this (Save method accordingly):
procedure TConfiguration.Load (const FileName : String);
const
PropNotFound = '_PROP_NOT_FOUND_';
var
IniFile : TIniFile;
Count : Integer;
List : PPropList;
TypeName, PropName, InputString, MethodName : String;
LoadMethod : TCustomLoadMethod;
begin
IniFile := TIniFile.Create (FileName);
try
Count := GetPropList (Self.ClassInfo, tkProperties, nil) ;
GetMem (List, Count * SizeOf (PPropInfo)) ;
try
GetPropList (Self.ClassInfo, tkProperties, List);
for I := 0 to Count-1 do
begin
TypeName := String (List [I]^.PropType^.Name);
PropName := String (List [I]^.Name);
InputString := IniFile.ReadString ('Options', PropName, PropNotFound);
if (InputString = PropNotFound) then
Continue;
MethodName := 'Load' + TypeName;
LoadMethod := Self.MethodAddress (MethodName);
if not Assigned (LoadMethod) then
raise EConfigLoadError.Create ('No load method for custom type ' + TypeName);
LoadMethod (Self, InputString);
end;
finally
FreeMem (List, Count * SizeOf (PPropInfo));
end;
finally
FreeAndNil (IniFile);
end;
The base class could provide load and save methods for the delphi default types. I can then create a configuration for my application like this:
TMyConfiguration = class (TConfiguration)
...
published
function SaveTObject (P : Pointer) : String;
procedure LoadTObject (const Str : String);
published
property BoolOption : Boolean read FBoolOption write FBoolOption;
property ObjOption : TObject read FObjOption write FObjOption;
end;
Example of a custom save method:
function TMyConfiguration.SaveTObject (P : Pointer) : String;
var
Obj : TObject;
begin
Obj := TObject (P);
Result := Obj.ClassName; // does not make sense; only example;
end;
I use XML for all my application as means of configuration. It is:
I have an XML library that makes it extremely easy to read or modify configuration, without even having to watch for missing values. Now you can also map the XML to a class inside application for faster access if speed is the issue, or certain values are read constantly.
I find other configuration methods far less optional:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With