How to auto-generate getter and setter methods - delphi

I'm a Java developer and I always use the getter-setter methods. How can I use this concept in Delphi?
I define a local variable //1
I create a property //2
I press CTRL+SHIFT+C and the editor creates the getter and setter methods //3
for this example:
unit Unit1;
type
ClassePippo=class
private
colorText:string; //1
function getColorText: String; //3
procedure setColorText(const Value: String); //3
public
property colore: String read getColorText write setColorText; //2
end;
implementation
{ ClassePippo }
function ClassePippo.getColorText: String; //3
begin
Result:=colorText;
end;
procedure ClassePippo.setColorText(const Value: String); //3
begin
colorText:=Value;
end;
end.
Is there a feature to auto-create the getter and setter methods?
I only want to write colorText: string; //1 and invoke a shortcut and I want that the IDE auto-creates //2 and //3.
(When I develop in Java using Eclipse I can auto-generate the getter and setter methods using Source-->Generate getter and setter...)

Type out the property you want first rather than the internal variable. Just create type the following in your class So
Property Colore : String Read GetColorText Write SetColorText;
then press Ctrl Shift C
the IDE will then create the getter, the setter and the private internal variable.
Note that Property setters and getters are optional in Object Pascal. You can just as easily write
Property Colore : String Read FColorText Write FColorText;
or have just a setter or getter
Property Colore : String Read FColorText Write SetColorText;
In this case the IDE will generate the private FColorText variable and a setter method SetColorText

There is no feature in the IDE which will do what you want.
You can use Ctrl + Shift + C to generate getters and setters from a property declaration. But those getters and setters are empty stubs.
Frankly that is quite reasonable behaviour in my view. How can the IDE be expected to know how you wish to implement your getter and setter. It cannot be expected to know which field you intend to use. Your code is a good demonstration of why that is so. There is no obvious algorithmic relationship between the property name and the field name.
Another point to make is that if you want the IDE to generate the code for getter and setter in your question automatically, why do you even bother with a getter and setter? You could perfectly well write:
property colore: string read ColorText write ColorText;
If you wish to be able to use a feature as you describe you will need to find an extension to Delphi that implements the feature. The obvious candidates are CnPack and GExperts. Or if you cannot find such an extension, write one yourself.

You can do this in three steps.
Define the property with a getter and a variable to hold the value:
property OnOff: Boolean Read GetOnOff Write fOnOff;
Press Control + Shift + C to generate the variable and getter sub:
private
fOnOff: Boolean;
function GetOnOff: Boolean;
...
And
function TMyClass.GetOnOff: Boolean;
begin
Result := fOnOff;
end;
Now change the property to read the variable and write with a setter:
property OnOff: Boolean Read fOnOff Write SetOnOff;
Press Control + Shift + C again to generate the setter sub:
procedure TMyClass.SetOnOff(const Value: Boolean);
begin
fOnOff := Value;
end;
Now change the property to use the getter and setter:
property OnOff: Boolean Read GetOnOff Write SetOnOff;
Yes, it's a bit convoluted but if you have a lot of properties to define at once it can help.

Related

Automatically serializing a TObject to JSON using mormot

I am trying to serialize an TObject to JSON using the mORMot framework. Unfortunately, the result is always null.
The class I am trying to serialize is:
type ApmTime = class(TObject)
private
function currentTime() : String;
published
property Current_time: String read currentTime;
public
constructor Create;
end;
constructor ApmTime.Create;
begin
inherited;
end;
function ApmTime.currentTime() : String;
begin
result := TimeToStr(Now);
end;
And the corresponding mORMot method is defined in SynCommons:
currentTime := ApmTime.Create;
Write(ObjectToJSON(currentTime, [woFullExpand]));
This always returns null. After having single-stepped in TTextWriter.WriteObject (located in unit SynCommons), the following piece of code seems to be where the resulting json is set to null:
if not(woFullExpand in Options) or
not(Value.InheritsFrom(TList)
{$ifndef LVCL} or Value.InheritsFrom(TCollection){$endif}) then
Value := nil;
if Value=nil then begin
AddShort('null');
exit;
I am expecting something along the line:
{
"Current_time" : "15:04"
}
Ran into this yesterday and worked out what's going on, so for the benefit of future people stumbling over this problem as well:
If you only add SynCommons.pas to your uses clause, then the default DefaultTextWriterJSONClass is set to TTextWriter which only supports serializing particular class types as you've seen, and doesn't support arbitrary classes/objects. See the following lines in SynCommons.pas where this default is set:
var
DefaultTextWriterJSONClass: TTextWriterClass = TTextWriter;
Now, in order to support serializing arbitrary objects to JSON, this global variable needs to be changed from the default TTextWriter to TJSONSerializer.
This class is defined in mORMot.pas, and in fact, if you add mORMot.pas to your uses clause, its initialization will override the above default and set TJSONSerializer as the new default for you.
This behaviour is in fact documented in SynCommons.pas if you read carefully enough, e.g. see the comments for "SetDEfaultJSONClass()" class method:
// you can use this method to override the default JSON serialization class
// - if only SynCommons.pas is used, it will be TTextWriter
// - but mORMot.pas initialization will call it to use the TJSONSerializer
// instead, which is able to serialize any class as JSON
class procedure SetDefaultJSONClass(aClass: TTextWriterClass);
So in short: To fix your issue, just add mORMot.pas to your uses clause in addition to SynCommons.pas which you should already have.
Try add a write to the published property.
property Current_time: String read currentTime write SetCurrentTime.
A readonly property is not serialized. Also ApmTime should be based on TPersistent
type
ApmTime = class(TPersistent)

Convert Integer to Enum in property getter/setter

I currently have an option management which works with properties, that use all the same getter/setter method but with an index.
This works fine for Ints and Bools, but I'd like to get this working for enums, too.
TIntegerOptions = (kInt1, kInt2, kInt3);
class property Integer1: Integer index kInt1 read GetOptionsValueInt write SetOptionsValueInt;
class property Integer2: Integer index kInt2 read GetOptionsValueInt write SetOptionsValueInt;
class property Integer3: Integer index kInt3 read GetOptionsValueInt write SetOptionsWertInt;
FOptionsListInt: array[TIntegerOptions] of IniIntObject; // Objects contain Inifile read&save functionalities
...
class function GetOptionsValueInt(const aOption: TIntegerOptions) : Integer
begin
Result := Ord(FOptionsListInt[aOption].GetValue());
end;
class procedure SetOptionsValueInt(const aOption: TIntegerOptions; const aValue: Integer);
begin
FOptionslistInt[aOption].ChangeTo(aValue);
end;
This is working so far, and now my problem:
TEnumOptions = (kEnum1, kEnum2, kEnum3);
TEnum1 = (e1o1, e1o2, e1o3);
TEnum2 = (e2o1, e2o2, e2o3);
TEnum3 = (e3o1, e3o2, e3o3);
// these props fail because my functions return / excpect Integers, not the matching Enums
class property Enum1: TEnum1 index kEnum1 read GetOptionsValueInt write SetOptionsValueEnum;
class property Enum2: TEnum2 index kEnum2 read GetOptionsValueInt write SetOptionsValueEnum;
class property Enum3: TEnum3 index kEnum3 read GetOptionsValueInt write SetOptionsValueEnum;
FOptionsListEnum: array[TEnumOptions] of IniIntObject; // Objects contain Inifile read&save functionalities
I marked the problems in the code. Can I somehow cast the Integer values returned by the getters/setters to the matching enum for each property?
For those who read the old question:
I decided to just use the same getter/setter for the enums, cause in the end they get saved as Integers, too. If I need to cast inside the getters somehow, I'll add them again, but i hoped for a solution inside the property-declaration.
Regarding your update, you want to use code like this:
class property Enum1: TEnum1 index kEnum1 read GetOptionsValueInt;
That fails to compile because GetOptionsValueInt returns a value of type Integer. Since the property has type TEnum1, that is a simple type mismatch. In order to have a property of type TEnum1, the getter must be a function that returns a value of type TEnum1.
If each of these properties has a different type, then you cannot share getter and setter. Shared indexed getters and setters can only be used for properties that share a common type. Since it looks like none of these properties share a type, you will not be able to share getters and setters.
Original answer to original question
You don't show all the declarations, so it's hard for us to know why your code does not compile. We don't know what FOptionsListeEnum is, and we don't know what Wert() is.
However, here's a complete program that demonstrates that enumerated types can be used as indexes for indexed properties:
{$APPTYPE CONSOLE}
type
TMyEnum = (evOne, evTwo);
TMyClass = class
private
class function GetValue(const Option: TMyEnum): Integer; static;
public
class property ValueOne: Integer index evOne read GetValue;
class property ValueTwo: Integer index evTwo read GetValue;
end;
class function TMyClass.GetValue(const Option: TMyEnum): Integer;
begin
Result := ord(Option);
end;
begin
Writeln(TMyClass.ValueOne);
Writeln(TMyClass.ValueTwo);
end.
So, that makes it clear that there's no issue using enumerated types as indices. In which case, what is your problem. Let's look at the code you have:
class function TKmpOption.GetOptionsWertEnum(const aOption: TEnumOptionen): Integer;
begin
Result := Ord(FOptionsListeEnum[aOption].Wert());
end;
class procedure TKmpOption.SetOptionsWertEnum(const aOption: TEnumOptionen; const aWert: Integer);
begin
FOptionslisteEnum[aOption].Aendern(aOption(aWert));
end;
If Ord(FOptionsListeEnum[aOption].Wert()) does not compile with a type mismatch error, then it would seem that Wert() is not an ordinal value.
As for aOption(aWert) that makes no sense at all. That can never compile. Perhaps you meant TEnumOptionen(aWert) but that also makes no sense. Why would the value be of the same type used to index the possible options.
I hope that the code above shows that the issue is not related to enumerated types as indexers and is in fact of a rather more prosaic nature.

Use Rtti to set method field

I'm using Delphi XE to write a base class, which will allow descending classes to have dll methods mapped by applying an annotation. However I get a typecasting error, which is understandable.
In essence the base class should look like this:
TWrapperBase = class
public
FLibHandle: THandle;
procedure MapMethods;
end;
procedure TWrapperBase.MapMethods;
var
MyField: TRttiField;
MyAttribute: TCustomAttribute;
pMethod: pointer;
begin
FLibHandle := LoadLibrary(PWideChar(aMCLMCR_dll));
for MyField in TRttiContext.Create.GetType(ClassType).GetFields do
for MyAttribute in MyField.GetAttributes do
if MyAttribute.InheritsFrom(TMyMapperAttribute) then
begin
pMethod := GetProcAddress(FLibHandle, (MyAttribute as TMyMapperAttribute).TargetMethod);
if Assigned(pMethod) then
MyField.SetValue(Self, pMethod); // I get a Typecast error here
end;
And a descending class could look like this:
TDecendant = class(TWrapperBase)
private type
TSomeDLLMethod = procedure(aParam: TSomeType); cdecl;
private
[TMyMapperAttribute('MyDllMethodName')]
FSomeDLLMethod: TSomeDLLMethod;
public
property SomeDLLMethod: TSomeDLLMethod read FSomeDLLMethod;
end;
I could implement this differently, by hard coding the linking for each method in an overriden 'MapMethods'. This would however require each descendant to do so which I'd like to avoid.
I know that the TValue as used in this case will contain a pointer and not of the correct type (procedure(aParam: TSomeType); cdecl; in this case).
My question: Is there a way to pass the pointer from 'GetProcAdress' as the correct type, or to set the field directly (for example by using the field address 'PByte(Self)+MyField.Offset', which you can use to set the value of a record property)?
With the old Rtti, this could be done but only for published properties and without any type checking:
if IsPublishedProp(Self, 'SomeDLLMethod') then
SetMethodProp(Self, 'SomeDLLMethod', GetProcAddress(FLibHandle, 'MethodName');
There are two problems:
First your EInvalidCast is caused by TValue being very strict about type conversions. You are passing in a Pointer and want to set a field of type TSomeDLLMethod. You need to explicitly pass a TValue that has the correct type info.
if Assigned(pMethod) then
begin
TValue.Make(#pMethod, MyField.FieldType.Handle, value);
MyField.SetValue(Self, value);
end;
Now you will run into another EInvalidCast exception which is triggered because of a bug in XE inside the GetInlineSize method of the Rtti.pas which returns 0 for a tkProcedure kind of type. I don't know in what version this got fixed but it does not exist anymore in XE5.
For XE this can be fixed by using a unit I wrote some while ago (and which I just updated to fix this bug): RttiPatch.pas.
I also reported the original issue because Pointer is assignment compatible to a procedure type so TValue should also handle this: http://qc.embarcadero.com/wc/qcmain.aspx?d=124010
You could try something like:
Move(pMethod, PByte(Self) + Field.Offset, SizeOf(Pointer));
or
PPointer(PByte(Self) + Field.Offset)^ := pMethod;

How to give setter a 2nd parameter in Delphi?

I want to know whether we can do such in Delphi:
I have a private procedure:
procedure SetMySend(const oValue: TTM_MySend_Profile;
displayValue: string = '...');
I have a public property:
property MySend: TTM_MySend_Profile displayLocateID '...'
read FMySend write SetMySend;
Can I give a parameter displayValue here as the 2nd parameter of the setter? I cannot get this compiled.
I cannot figure out the correct way to do it and wonder whether I can do this in Delphi. Thanks for help!
A property setter for a property takes only one parameter, of the same type as the property. There is no syntax that would allow you to write the type of code you are attempting to write. Note that I am ignoring array properties which are not pertinent here.
What you need to do is to write a dedicated setter which supplies the extra parameter to your SetMySend function.
procedure SetMySend(const Value: TTM_MySend_Profile;
const displayValue: string); overload;
procedure SetMySend(const Value: TTM_MySend_Profile); overload;
property MySend: TTM_MySend_Profile read FMySend write SetMySend;
And then in the implementation you write
procedure TMyClass.SetMySend(const Value: TTM_MySend_Profile);
begin
SetMySend(Value, '...');
end;
You could hijack index specifiers to effect something similar, but I would not recommend that.

Delphi - records with variant parts

I want to have a record (structure) with a 'polymorphic' comportment. It will have several fields used in all the cases, and I want to use other fields only when I need them. I know that I can accomplish this by variant parts declared in records. I don't know if it is possible that at design time I can access only the elements I need. To be more specific, look at the example bellow
program consapp;
{$APPTYPE CONSOLE}
uses
ExceptionLog,
SysUtils;
type
a = record
b : integer;
case isEnabled : boolean of
true : (c:Integer);
false : (d:String[50]);
end;
var test:a;
begin
test.b:=1;
test.isEnabled := False;
test.c := 3; //because isenabled is false, I want that the c element to be unavailable to the coder, and to access only the d element.
Writeln(test.c);
readln;
end.
Is this possible?
All variant fields in a variant record are accessible at all times, irrespective of the value of the tag.
In order to achieve the accessibility control you are looking for you would need to use properties and have runtime checks to control accessibility.
type
TMyRecord = record
strict private
FIsEnabled: Boolean;
FInt: Integer;
FStr: string;
// ... declare the property getters and settings here
public
property IsEnabled: Boolean read FIsEnabled write FIsEnabled;
property Int: Integer read GetInt write SetInt;
property Str: string read GetString write SetString;
end;
...
function TMyRecord.GetInt: Integer;
begin
if IsEnabled then
Result := FInt
else
raise EValueNotAvailable.Create('blah blah');
end;
Even if I heard that by original Niklaus Wirth's Pascal definition all should work as you expected, I saw no such behaviour in Delphi, starting from its ancestor, Turbo Pascal 2.0. Quick look at FreePascal showed that its behaviour is the same. As said in Delphi documentation:
You can read or write to any field of any variant at any time; but if you write to a field in one variant and then to a field in another variant, you may be overwriting your own data. The tag, if there is one, functions as an extra field (of type ordinalType) in the non-variant part of the record."
Regarding your intent, as far as I understood it, I would use two different classes, kind of
a = class
b : Integer
end;
aEnabled = class(a)
c: Integer
end;
aDisabled = class(a)
d: String //plus this way you can use long strings
end;
This way you can get some support from IDE's Code Editor even at designtime. More useful, though, is that it will be much more easier to modify and support later.
However, if you need quick switching of record variable values at runtime, #David Heffernan's variant , to use properties and have runtime checks, is more reasonable.
The example given is NOT a variant record, it includes all the fields all the time.
A true variant record has the variants sharing the same memory. You just use the "case discriminator: DiscType of ..... " syntax, no need for a separate field telling you what variant is active.

Resources