Duplicate identifier of property and method parameter of a class - delphi

I transferred my project from Delphi to Lazarus. In a form I have a private method with parameter var Active: Boolean. In Delphi it was ok, but Lazarus give an error Error: Duplicate identifier "Active" and Hint: Identifier already defined in unit FORMS at line 641, on line 641 there is:
property Active: Boolean read FActive;
It is not difficult to change parameter name (with refactoring), but why can't I use the same name for property and parameter of method?
To make sure it is not an error of automatic conversion from Delphi, I created new project in Lazarus and added private method
procedure Test(var Active: Boolean);
The result was the same. Even if I use const or nothing instead of var.
I've looked into FPC docs and didn't find any such limitations. I'm just curious.

You should be able to use the same name for a property and a parameter. They have different scope, so the one nearest in scope (the parameter, which should be treated as being in the same scope as a local variable) should hide the one "further away" in scope (the property). In Delphi, you can still access the property, even inside that method, but then you should qualify it as Self.Active:
procedure TForm1.Test(var Active: Boolean);
var
ParamActive: Boolean;
FormActive: Boolean;
begin
ParamActive := Active; // gets the var parameter
FormActive := Self.Active; // gets the property
...
end;
I have no idea why FPC flags it as an error. It shouldn't.
Update
FWIW, if you change
{$mode objfpc}
to
{$mode delphi}
It does compile as expected, and you won't get an error. I just tried this.

Related

Value used as both output and (non-reference) input in a chain of methods

Consider the following minimal example of method chaining, where a floating-point variable is set (using an out parameter) by an early method and then passed (using a const parameter) to a later method in the chain:
program ChainedConundrum;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
ValueType = Double;
TRec = record
function GetValue(out AOutput: ValueType): TRec;
procedure ShowValue(const AInput: ValueType);
end;
function TRec.GetValue(out AOutput: ValueType): TRec;
begin
AOutput := 394;
Result := Self;
end;
procedure TRec.ShowValue(const AInput: ValueType);
begin
Writeln(AInput);
end;
var
R: TRec;
Value: ValueType = 713;
begin
R.GetValue(Value).ShowValue(Value);
Readln;
end.
I initially expected this to print the floating-point number 394 (in some format), but it doesn't (necessarily); when I build the program using the 32-bit compiler of Delphi 10.3.2, the program prints 713. Stepping through the program using the debugger confirms that the initial, pre-GetValue value of Value is passed to ShowValue.
However, if I build this using the 64-bit compiler, 394 is printed. Similarly, if I change ValueType from Double to Int32, I get 394 in both versions. Int64 yields 394 in 64-bit and 713 in 32-bit. Strings yield the updated value. Classes work just like records. Class methods, however, as opposed to instance methods, always give me the updated value. And, of course, abandoning method chaining (R.GetValue(Value); R.ShowValue(Value)) does the same.
Not surprisingly, if I change the AInput parameter of ShowValue from a const (or undecorated value) parameter to a var parameter, I always get the updated value.
My conclusion is that either
it is not allowed to both set and pass a variable in a chain of methods like this, or
there's a bug in the compiler.
My question is: which is it? And if it isn't allowed, where does the documentation state this? I have so far not been able to find the relevant passage. (The phrase "sequence point" seems very rarely to occur anywhere near the phrase "Delphi" on the WWW.)
Everyone who has commented on this issue here or elsewhere agrees that this either "feels like" or "clearly" is a compiler bug.
I've created issue RSP-29733 at the Embarcadero Jira.
Turning to possible workarounds, notice that the issue seems to be that an old value of a variable is used by the compiler. Hence, the problem arise when the value is changed close to the use of the variable.
However, the variable's address isn't changed, so if you pass the variable by reference instead of by value, the problem disappears. One way is to use a var parameter when the value is passed the second time, even though you don't need that, or even want that semantically.
Hence, a more natural approach seems to be to use a const [Ref] parameter:
procedure ShowValue(const [Ref] AInput: ValueType);
This has the same semantics as an undecorated const parameter but forces the compiler to pass the variable by reference, thus avoiding the bug.

Is it always safe to remove a published empty section?

I'm working on an old legacy project which have several classes in which the published section is always declared without anything inside of it, i.e:
TMyClass = class
public
procedure DoSomething();
published
end;
On compiling, I get the following warning message:
[DCC Warning] uMyUnit.pas(141): W1055 PUBLISHED caused RTTI ($M+) to
be added to type 'TMyClass'
I don't know if the predecessor developer has declared these published sections for some valid reason.
Is it always safe to remove an empty published section or could it cause some changes in the application's behavior?
The difference for the class itself is none - however the important thing is that the default visibility of any class that inherits from a class with {$M+} then changes from public to published!
See this example code:
uses
TypInfo;
type
TMyClass = class
private
fName: string;
property Name: string read fName;
published
end;
TMyOtherClass = class(TMyClass)
property Name;
end;
var
propCount, i: Integer;
props: PPropList;
begin
propCount := GetPropList(TypeInfo(TMyOtherClass), props);
for i := 0 to propcount - 1 do
Writeln(props^[i].Name);
Readln;
end.
You can see that it lists the Name property but when you remove the published from TMyClass it will not - that is because once TMyClass got {$M+} added any member declared without explicitly stating the visibility it will be published opposed to public.
Also other members declared without visibility like fields will be published. This is being used in the streaming system Delphi uses for forms and such.
You can for example then call TObject.FieldAddress or TObject.MethodAddress passing in the name of a field or method and get back pointers to the field or method. It only works with published fields and methods.
This is how loading from a dfm sets up all those IDE generated fields like Button1 or connects the Button1Click method to the Button1.OnClick - they are without explicit visibility at the top of your form which inherits from TComponent that has {$M+} declared.
It depends
Does the rest of the code actually require access to RTTI for the class?
Only TPersistent-derived classes have the {$M+} directlive applied to them by default without needing a published section.
A published section is used for DFM streaming, which requires RTTI. Non-persistent classes are not steamed in DFMs, but there are other uses for RTTI.
So, without knowing what the rest of the code does, it is not really known whether removing empty published sections is safe or not.

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;

getting "E2197 Constant object cannot be passed as var parameter" when passing var parameter

this code works fine:
procedure TForm2.Timer1Timer(Sender: TObject);
var
Text: string;
begin SetLength (Text,555);
GetWindowText (getforegroundwindow, PChar (Text),555);
Form2.gtListBox1.Items.Add (
IntToStr (getforegroundwindow) + ': ' + Text);
end;
but when i put
var
Text: string;
from Timer1Timer event handler to
units implementation section or ''text : string'' in the units var section i get error : E2197 Constant object cannot be passed as var parameter
according to documentation :
This error message appears when you
try to send a constant as a var or out
parameter for a function or procedure.
but i didnt declared text as constant then why am i getting this error?
Edit:#mason wheeler: i do not understand than why does this work:
implementation
{$R *.dfm}
var
char :integer;//first of all why does delphi let me declare variable that is also a type name
procedure TForm2.Button1Click(Sender: TObject);
begin
char:=11;
showmessage(IntToStr(char));
end;
my first code was not working because i declared text as string ,you say : ''the compiler might think it's a reference to the type and not to the variable'' than why doesnt the compiler think its a reference to the type and not to the variable in this case? i am confused
Edit2: i now understand what was wrong but still have 1 confusion i did'nt use a with statement then why delphi is treating as if i am using:
with
form1 do
text := 'blahblahblah';
this is wrong on the delphi part i mean delphi should not let us do text := 'blah' but form1.text := blah; or with form1 do text := 'blah'; do i need to turn on/off some compiler setting(s) i am using delphi 2010 without any ide experts
Actually if you declare Text in implementation section and use it in Timer1Timer(Sender: TObject), compiler will consider Text as Form1.Text.
Change the name of text as sText and it will work.
Edit 1:
Because there is no property/Field for form like Form1.Char.
It's probably a name confusion. "Text" is a type name as well, a legacy textfile type. So if you declare the variable in a different scope, the compiler might think it's a reference to the type and not to the variable. Try naming it something else and it should work.
With regard to your Edit #2:
That's a standard convention of object-oriented programming. When you're writing a method for an object, the code is implicitly interpreted as being in the scope of the object. In other words, every object method can be considered as being inside a hidden with self do block.

How to know what type is a var?

TypeInfo(Type) returns the info about the specified type, is there any way to know the typeinfo of a var?
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= TypeInfo(S);
Info:= TypeInfo(Instance);
Info:= TypeInfo(Obj);
end
This code returns:
[DCC Error] Unit1.pas(354): E2133 TYPEINFO standard function expects a type identifier
I know a non instantiated var is only a pointer address.
At compile time, the compiler parses and do the type safety check.
At run time, is there any way to know a little more about a var, only passing its address?
No.
First, there's no such thing as a "non-instantiated variable." You instantiate it by the mere act of typing its name and type into your source file.
Second, you already know all there is to know about a variable by looking at it in your source code. The variable ceases to exist once your program is compiled. After that, it's all just bits.
A pointer only has a type at compile time. At run time, everything that can be done to that address has already been determined. The compiler checks for that, as you already noted. Checking the type of a variable at run time is only useful in languages where a variable's type could change, as in dynamic languages. The closest Delphi comes to that is with its Variant type. The type of the variable is always Variant, but you can store many types of values in it. To find out what it holds, you can use the VarType function.
Any time you could want to use TypeInfo to get the type information of the type associated with a variable, you can also directly name the type you're interested in; if the variable is in scope, then you can go find its declaration and use the declared type in your call to TypeInfo.
If you want to pass an arbitrary address to a function and have that function discover the type information for itself, you're out of luck. You will instead need to pass the PTypeInfo value as an additional parameter. That's what all the built-in Delphi functions do. For example, when you call New on a pointer variable, the compiler inserts an additional parameter that holds the PTypeInfo value for the type you're allocating. When you call SetLength on a dynamic array, the compiler inserts a PTypeInfo value for the array type.
The answer that you gave suggests that you're looking for something other than what you asked for. Given your question, I thought you were looking for a hypothetical function that could satisfy this code:
var
S: string;
Instance: IObjectType;
Obj: TDBGrid;
Info: PTypeInfo;
begin
Info:= GetVariableTypeInfo(#S);
Assert(Info = TypeInfo(string));
Info:= GetVariableTypeInfo(#Instance);
Assert(Info = TypeInfo(IObjectType));
Info:= GetVariableTypeInfo(#Obj);
Assert(Info = TypeInfo(TDBGrid));
end;
Let's use the IsClass and IsObject functions from the JCL to build that function:
function GetVariableTypeInfo(pvar: Pointer): PTypeInfo;
begin
if not Assigned(pvar) then
Result := nil
else if IsClass(PPointer(pvar)^) then
Result := PClass(pvar).ClassInfo
else if IsObject(PPointer(pvar)^) then
Result := PObject(pvar).ClassInfo
else
raise EUnknownResult.Create;
end;
It obviously won't work for S or Instance above, but let's see what happens with Obj:
Info := GetVariableTypeInfo(#Obj);
That should give an access violation. Obj has no value, so IsClass and IsObject both will be reading an unspecified memory address, probably not one that belongs to your process. You asked for a routine that would use a variable's address as its input, but the mere address isn't enough.
Now let's take a closer look at how IsClass and IsObject really behave. Those functions take an arbitrary value and check whether the value looks like it might be a value of the given kind, either object (instance) or class. Use it like this:
// This code will yield no assertion failures.
var
p: Pointer;
o: TObject;
a: array of Integer;
begin
p := TDBGrid;
Assert(IsClass(p));
p := TForm.Create(nil);
Assert(IsObject(p));
// So far, so good. Works just as expected.
// Now things get interesting:
Pointer(a) := p;
Assert(IsObject(a));
Pointer(a) := nil;
// A dynamic array is an object? Hmm.
o := nil;
try
IsObject(o);
Assert(False);
except
on e: TObject do
Assert(e is EAccessViolation);
end;
// The variable is clearly a TObject, but since it
// doesn't hold a reference to an object, IsObject
// can't check whether its class field looks like
// a valid class reference.
end;
Notice that the functions tell you nothing about the variables, only about the values they hold. I wouldn't really consider those functions, then, to answer the question of how to get type information about a variable.
Furthermore, you said that all you know about the variable is its address. The functions you found do not take the address of a variable. They take the value of a variable. Here's a demonstration:
var
c: TClass;
begin
c := TDBGrid;
Assert(IsClass(c));
Assert(not IsClass(#c)); // Address of variable
Assert(IsObject(#c)); // Address of variable is an object?
end;
You might object to how I'm abusing these functions by passing what's obviously garbage into them. But I think that's the only way it makes sense to talk about this topic. If you know you'll never have garbage values, then you don't need the function you're asking for anyway because you already know enough about your program to use real types for your variables.
Overall, you're asking the wrong question. Instead of asking how you determine the type of a variable or the type of a value in memory, you should be asking how you got yourself into the position where you don't already know the types of your variables and your data.
With generics, it is now possible to get the type info without specifying it.
Certain users indicated the following code doesn't compile without errors.
As of Delphi 10 Seattle, version 23.0.20618.2753, it compiles without errors, as seen below in the screenshot.
program TypeInfos;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.TypInfo;
type
TTypeInfo = class
class procedure ShowTypeInfo<T>(const X: T);
end;
{ TTypeInfo }
class procedure TTypeInfo.ShowTypeInfo<T>(const X: T);
var
LTypeInfo: PTypeInfo;
begin
LTypeInfo := TypeInfo(T);
WriteLn(LTypeInfo.Name);
end;
var
L: Exception;
B: Boolean;
begin
// Console output
TTypeInfo.ShowTypeInfo(L); // Exception
TTypeInfo.ShowTypeInfo(B); // Boolean
end.
Not that I know of. You can get RTTI (Run Time Type Information) on published properties of a class, but not for "normal" variables like strings and integers and so forth. The information is simply not there.
Besides, the only way you could pass a var without passing a type is to use either a generic TObject parameter, a generic type (D2008, as in ), or as an untyped parameter. I can't think of another way of passing it that would even compile.

Resources