My light version of a Command-line parsing with Delphi attribute ; it's a Primož Gabrijelčič 's idea, found in ITDevCon 2013 Pdf. (no source so I writed it!)
My light version of a Command-line parsing with Delphi attribute ; it's a Primož Gabrijelčič 's idea, found in ITDevCon 2013 Pdf. (no source so I writed it!)
interface
uses rtti;
type
ParamNameAttribute = class(TCustomAttribute)
protected
FName:string;
FDefaultValue:TValue;
public
constructor Create(const AName:string);overload;
constructor Create(const AName:string; const ADefaultValue:string);overload;
constructor Create(const AName:string; const ADefaultValue:boolean);overload;
constructor Create(const AName:string; const ADefaultValue:Integer);overload;
property Name:string read FName;
property DefaultValue:TValue read FDefaultValue;
end;
TCmdLine = class(TObject)
public
constructor Create;
end;
implementation
uses sysutils;
{ ParamNameAttribute }
constructor ParamNameAttribute.Create(const AName: string);
begin
FName := AName;
end;
constructor ParamNameAttribute.Create(const AName, ADefaultValue: string);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
constructor ParamNameAttribute.Create(const AName: string;
const ADefaultValue: boolean);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
constructor ParamNameAttribute.Create(const AName: string;
const ADefaultValue: Integer);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
{ TCmdLine }
// the core
constructor TCmdLine.Create;
var
ctx:TRttiContext;
t:TRttiType;
field:TRttiField;
attr:TCustomAttribute;
param:ParamNameAttribute;
value:string;
bfound:boolean;
begin
ctx := TRttiContext.Create;
try
t := ctx.GetType(Self.ClassType);
for field in t.GetFields do
for attr in field.GetAttributes do
if attr is ParamNameAttribute then
begin
param := ParamNameAttribute(attr);
value := '';
bfound := FindCmdLineSwitch(param.Name, value, true, [clstValueAppended]);
if bfound then
begin
if field.FieldType.Handle = typeinfo(boolean) then
field.SetValue(Self, true)
else
field.SetValue(Self, value);
end
else
if not param.DefaultValue.IsEmpty then
field.SetValue(Self, param.DefaultValue);
end;
finally
ctx.Free;
end;
end;
It's simple and very useful :
example :
TMyCmdLine = class(TCmdLine)
protected
[ParamName('importdir','c:\tmp\')]
FImportFolder: string;
[ParamName('logsoapdetails')]
FLogDetails: boolean;
[ParamName('user', 'guest')]
FUsername: string;
[ParamName('debug')]
FDebug:boolean;
public
property ImportFolder:string read FImportFolder;
property LogDetails:boolean read FLogDetails;
property Username:string read FUsername;
property Debug:boolean read FDebug;
end;
and use :
Cmd := TMyCmdLine.Create;
Cmd.Username...
if Cmd.Debug then ....
#attribute #delphi
interface
uses rtti;
type
ParamNameAttribute = class(TCustomAttribute)
protected
FName:string;
FDefaultValue:TValue;
public
constructor Create(const AName:string);overload;
constructor Create(const AName:string; const ADefaultValue:string);overload;
constructor Create(const AName:string; const ADefaultValue:boolean);overload;
constructor Create(const AName:string; const ADefaultValue:Integer);overload;
property Name:string read FName;
property DefaultValue:TValue read FDefaultValue;
end;
TCmdLine = class(TObject)
public
constructor Create;
end;
implementation
uses sysutils;
{ ParamNameAttribute }
constructor ParamNameAttribute.Create(const AName: string);
begin
FName := AName;
end;
constructor ParamNameAttribute.Create(const AName, ADefaultValue: string);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
constructor ParamNameAttribute.Create(const AName: string;
const ADefaultValue: boolean);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
constructor ParamNameAttribute.Create(const AName: string;
const ADefaultValue: Integer);
begin
Create(AName);
FDefaultValue := ADefaultValue;
end;
{ TCmdLine }
// the core
constructor TCmdLine.Create;
var
ctx:TRttiContext;
t:TRttiType;
field:TRttiField;
attr:TCustomAttribute;
param:ParamNameAttribute;
value:string;
bfound:boolean;
begin
ctx := TRttiContext.Create;
try
t := ctx.GetType(Self.ClassType);
for field in t.GetFields do
for attr in field.GetAttributes do
if attr is ParamNameAttribute then
begin
param := ParamNameAttribute(attr);
value := '';
bfound := FindCmdLineSwitch(param.Name, value, true, [clstValueAppended]);
if bfound then
begin
if field.FieldType.Handle = typeinfo(boolean) then
field.SetValue(Self, true)
else
field.SetValue(Self, value);
end
else
if not param.DefaultValue.IsEmpty then
field.SetValue(Self, param.DefaultValue);
end;
finally
ctx.Free;
end;
end;
It's simple and very useful :
example :
TMyCmdLine = class(TCmdLine)
protected
[ParamName('importdir','c:\tmp\')]
FImportFolder: string;
[ParamName('logsoapdetails')]
FLogDetails: boolean;
[ParamName('user', 'guest')]
FUsername: string;
[ParamName('debug')]
FDebug:boolean;
public
property ImportFolder:string read FImportFolder;
property LogDetails:boolean read FLogDetails;
property Username:string read FUsername;
property Debug:boolean read FDebug;
end;
and use :
Cmd := TMyCmdLine.Create;
Cmd.Username...
if Cmd.Debug then ....
#attribute #delphi
Boian Mitov Your definition of code is flawed. If you declare what you want that is the code. Maybe not the kind of code as if I write for i := 0 to count-1 do whatever() but it still is code. And thus it needs to be tested because maybe I declared the wrong thing that does not do what the spec was. And until we have a programming language where the definition of the spec equals the code/declaration it will be a while if that ever happens.
ReplyDeleteStefan Glienke
ReplyDeleteCorrect, however such code is subject to different form of testing, and actually is easier to test, so it is a win, win ;-) . That is what we have been working on for the last 2 years, and we are well on the way to this goal :-) . I have reduced the development expense 3 fold already, sped up development by factor of 10, and reduced testing expenses by ~3 fold :-) Not bad! :-) . Now we do things different than Michel, but some of the approaches are similar.
Here is some similar code I wrote for INI files which can handle attributes on both properties and fields:
ReplyDelete- https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Library/RTL/TypeInformation/IniFilePersistenceUnit.pas
Example:
- https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Library/Indy/Email/SmtpSettingsUnit.pas
- https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Apps/Console/Email/SendEmlOverSmtp/Win32/Debug/SendEmlOverSmtp.ini
- https://bitbucket.org/jeroenp/besharp.net/src/tip/Native/Delphi/Apps/Console/Email/SendEmlOverSmtp/SendEmlOverSmtpUnit.pas