What does [ref] do in a VCL application? - delphi

I'm working on a VCL application with Delphi 10 Seattle, and created a TDBGrid event handler via the IDE when I noticed that Delphi added a Ref custom attribute for the Rect argument:
procedure TfrmXxx.yyyDrawColumnCell(Sender: TObject;
const [Ref] Rect: TRect; DataCol: Integer; Column: TColumn;
State: TGridDrawState);
begin
//
end;
When or why does the IDE decide to insert this?
Does it have any effect in a VCL app?
update
Here's a video for those who cannot reproduce the behavior:

It is mentioned in the docs:
Constant parameters may be passed to the function by value or by reference, depending on the specific compiler used. To force the compiler to pass a constant parameter by reference, you can use the [Ref] decorator with the const keyword.
See Constant Parameters
When or why does the IDE decide to insert this?
The IDE never inserts this. It just copies the declaration of the event handler. Whoever wrote the event handler put the pass by[ref]erence marker in there.
Does it have any effect in a VCL app?
Yes.
If you mark a 8 byte parameter as const it will normally get passed by value in x64 and passed by reference in x86.
Declaring it as const [ref] will force it to be passed by reference in both cases.
It is very useful when doing inline assembly and in multi-threaded code.
Before const [ref] was introduced we were forced to use var instead of const to achieve the same effect.

Related

Duplicate identifier of property and method parameter of a class

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.

Is the [Ref] attribute for const record parameters useful?

With the latest Delphi version (Berlin/10.1/24), is the [Ref] attribute really necessary?
I ask this because the online doc says:
Constant parameters may be passed to the function by value or by
reference, depending on the specific compiler used. To force the
compiler to pass a constant parameter by reference, you can use the
[Ref] decorator with the const keyword.
It's pretty much as described by the documentation. You'd use [ref] if you had a reason to enforce the argument to be passed by reference. One example that I can think of is for interop. Imagine you are calling an API function that is defined like this:
typedef struct {
int foo;
} INFO;
int DoStuff(const INFO *lpInfo);
In Pascal you might wish to import it like this:
type
TInfo = record
foo: Integer;
end;
function DoStuff(const Info: TInfo): Integer; cdecl; external libname;
But because TInfo is small, the compiler might choose to pass the structure by value. So you can annotate with [ref] to force the compiler to pass the parameter as a reference.
function DoStuff(const [ref] Info: TInfo): Integer; cdecl; external libname;
Another example is the new FreeAndNil procedure declaration in Delphi 10.4 in SysUtils.pas, which now finally ensures that only TObject descendants can be used with FreeAndNil. In previous Delphi versions, you could pass anything to this function, even if it didn't make sense.

Delphi 10 Seattle changes to Win32 GetPath and redundant TPoint and _POINTL record types

I am trying to port some code that works in Delphi XE8 to Delphi 10 Seattle. This code calls the GetPath function in Winapi.Windows.
The new Win32 API function signature is:
function GetPath(DC: HDC; Points: PPointL; Types: PByte; nSize: Integer): Integer; stdcall;
In XE8, previously the function had "var Points,Types" which is known commonly as a "var untyped" parameter.
Fixing the code to work with Delphi 10 Seattle means "unifying" the arbitrary types in the application code to use exactly the types declared in the unit itself. However what is confusing me is that there are two types, PPointL, and TPoint, and when I get the GetPath function working, the data it populates is populated into an array of _POINTL records, declared thus in Winapi.Windows:
type
_POINTL = record { ptl }
x: Longint;
y: Longint;
end;
{$EXTERNALSYM _POINTL}
PPointL = ^TPointL;
TPointL = _POINTL;
However, there is also another type TPoint, declared in System.Types:
TPoint = record
X: FixedInt;
Y: FixedInt;
public
Elsewhere, FixedInt is aliased to Longint for both 32 bit and 64 bit Windows, and so TPoint and _POINTL are equivalent, as far as I can tell, on the Windows platform at least.
If existing application component code is all using a type named TPoint, like this:
procedure AddPoint(const P:TPoint);
... What sense am I to make of the situation on the ground inside the RTL sources in Delphi 10? What should my approach to fixing this be? Alias TPoint to _POINTL at the unit level?
How do I fix this and proceed? Since this code is a commercial component, I'm thinking I'll wait until the vendor fixes this, but then, I think that understanding the _POINTL and TPoint in the RTL, and why these structures are redundantly/duplicated in definition, would help others porting low level Win32 Code from Delphi XE8 to Delphi 10 Seattle.
Update: As a workaround, I find I can re-declare an import of the function GetPath, and have it remain as var untyped in my own private unit implementation area import, and continue:
{$ifdef D23}
{$POINTERMATH ON}
// Delphi 10 Seattle: function GetPath(DC: HDC; Points: PPointL; Types: PByte; nSize: Integer): Integer; stdcall;
// previously had "var Points,Types" untyped,
const
gdi32 = 'gdi32.dll';
{$EXTERNALSYM GetPath}
function GetPath(DC: HDC; var Points, Types; nSize: Integer): Integer; stdcall; external gdi32 name 'GetPath';
{$endif}
There's not much to be said about this, beyond the fact that the change to Winapi.Windows.GetPath in DX Seattle is wrong. I mean, technically it will work, but it leaves any code that uses GetPath in an isolated silo.
This TPointL type is not new, but it is the wrong type for GetPath. The Win32 API function is:
int GetPath(
_In_ HDC hdc,
_Out_ LPPOINT lpPoints,
_Out_ LPBYTE lpTypes,
_In_ int nSize
);
And LPPOINT is POINT* and POINT maps to TPoint. There are some Win32 API functions that use POINTL, but the majority use POINT. Of course, Microsoft are not helping by having declared two identical types when one would suffice.
Very hard to see how the Embarcadero developer has managed to come up with POINTL in the new GetPath, but there you go. In my view you should submit a QP report and request that the declaration is changed from PPointL to PPoint.
In the meantime a simple cast will suffice because these two types are binary compatible. You wish to pass a PPoint, but the compiler wants PPointL. So pass PPointL(...) where ... is the expression that yields a PPoint.

Undocumented intrinsic routines

Delphi has this list: Delphi Intrinsic Routines
But that list is incomplete.
What are the 7 undocumented intrinsic functions, since when and what is their purpose?
I know of the following undocumented intrinsic functions.
Delphi 2007: here and Hallvard's blog:
Default
function Default(T: Typeidentifier): value of T;
Returns the zero representation of type identifier T.
The following intrinsics introduced in XE7 are explained in the XE7 beta blog and by Stefan Glienke
IsManagedType
function IsManagedType(T: TypeIdentifier): Boolean;
True if T is a interface, string or dynamic array, or a record containing such. A class containing a managed type will return false.
In XE6 and older you have to use System.Rtti.IsManaged(TypeInfo(T)).
HasWeakRef
function HasWeakRef(T: TypeIdentifier): Boolean;
True if T has been annotated as [weak]. The compiler keeps a list of [weak] references. You cannot use move and other tricks with these types, because that will prevent the weak-list from getting updated.
In XE6 and older you have to use System.TypInfo.HasWeakRef(TypeInfo(T)).
GetTypeKind
function GetTypeKind(T: TypeIdentifier): TTypeKind;
Does the same thing as PTypeInfo(System.TypeInfo(T))^.Kind;, however because it is a compiler intrinsic the function is resolved at compiletime and conditional code that evaluates to false will be stripped by the compiler.
IsConstValue
function IsConstValue(const Value): Boolean;
True if Value is a constant, false if not.
This helps the compiler to eliminate dead code because the function is evaluated at compile time.
This is only useful in inline functions, where it allows for shorter generated code.
TypeInfo
function TypeInfo(T: typeindentifier): PTypeInfo;
This function is not undocumented as such, but what is undocumented is that it is an intrinsic function since XE7.
That means that the snippet if TypeInfo(T) = TypeInfo(byte) then ... does not generate any code if T is not a byte and the test will be resolved at compiletime.
However the compile-time resolution only works inside generic routines and only when doing a if (TypeInfo(T) = TypeInfo(sometype) test.
The test if TypeInfo(byte) = TypeInfo(smallint) then does not get eliminated even though it always evaluates to false.
Nor does other use of TypeInfo(T).
ReturnAddress
The following are used with the raise exception at returnaddress construct.
function ReturnAddress(Expression): pointer; //Delphi ?
function AddressOfReturnAddress(Expression): pointer; //Delphi ?
And as far as I know you can't call them directly from user code.
Example of IsConstValue
type
TFlavor = (Tasty, Nasty);
TIntegerHelper = record helper for integer
function GetSomething(Flavor: TFlavor): TPoint; inline;
private
function GetTastyPoint: TPoint;
function GetNastyPoint: TPoint;
end;
function TIntegerHelper.GetSomething(Flavor: TFlavor): TPoint;
begin
if IsConstValue(Flavor) then begin
if Flavor = Tasty then Result:= Self.GetTastyPoint
else Result:= Self.GetNastyPoint;
end else begin
Assert(1=0, 'This function can only be called with constant parameters');
end;
end;
procedure Test;
var
pt: TPoint;
begin
pt:= 100000.GetSomething(Tasty);
This call will get translated to GetTastyPoint and the if/then sequence will be eliminated by the linker.

Is it safe to pass Delphi const string params across memory manager boundaries?

Subj. I'd like to use strings instead of PChar because that spares me much casting, but if I just do
procedure SomeExternalProc(s: string); external SOMEDLL_DLL;
and then implement it in some other project with non-shared memory manager:
library SeparateDll;
procedure SomeExternalProc(s: string);
begin
//a bla bla bla
//code here code here
end;
I have (formally) no guarantee Delphi does not decide for whatever reason to alter the string, modify its reference counter, duplicate or unique it, or whatever else. For example
var InternalString: string;
procedure SomeExternalProc(s: string);
begin
InternalString := s;
end;
Delphi increments refcounter and copies a pointer, that's it. I'd like Delphi to copy the data. Does declaring the parameter as "const" make it safe for that reason? If not, is there a way to do it? Declaring parameter as PChar doesn't seem to be a solution because you need to cast it every time:
procedure SomeExternalProc(s: Pchar); forward;
procedure LocalProc;
var local_s: string;
begin
SomeExternalProc(local_s); //<<--- incompatible types: 'string' and 'PAnsiChar'
end;
That would probably work, as long as you only ever use your DLL from code compiled in the same version of Delphi. The internal format of string has been known to change between releases, and you have no formal guarantee that it won't change again.
If you want to avoid having to cast everywhere you use it, try wrapping the function, like this:
procedure SomeExternalProc(s: Pchar); external dllname;
procedure MyExternalProc(s: string); inline;
begin
SomeExternalProc(PChar(local_s));
end;
Then in your code, you call MyExternalProc instead of SomeExternalProc, and everyone's happy.
If both the app and the DLL are written in the same Delphi release, just use shared memory manager (more details here).
If one side is written in a different language than there's no other way but to use PChar or WideString (WideStrings are managed by the COM memory manager).
Or you can write a wrapper function:
procedure MyExternalProc(const s: string);
begin
SomeExternalProc(PChar(s));
end;
Just to add a single fact:
Delphi allows you to simply assign PChar to a string so on the DLL side you don't need any typecast:
function MyDllFunction(_s: PChar): integer;
var
s: string;
begin
s := _s; // implicit conversion to string
// now work with s instead of the _s parameter
end;
This also applies for passing PChar as a parameter to a function that expects a (by value) string.
I recommend to use an alternative memory manager such as RecyclerMM or FastMM. They doesn't require any external shared MM dll's and allows you to pass strings to the dlls safely. As a bonus, you may get a nice performance improvement in whole application.
FastMM is used as a default memory manager in Delphi 2006 and above. Also it's a good tool to search the memory-leaks.

Resources