Undocumented intrinsic routines - delphi

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.

Related

E2250 There is no overloaded version of 'StrPas' that can be called with these arguments

My code :
function ThisModuleName: Char; //bulo String
var
p: array [0..512] of char;
fileNamePart: pchar;
begin
GetModuleFileName(HInstance, #p[0], 512);
GetFullPathName(#p[0], 512, #p[0], fileNamePart);
result := StrPas(WideString(#p[0])); //stalo WideString
end;
In Delphi 7 compiles.
In Delphi 10.2 it gives an error:
[dcc32 Error] verinfo.pas(98): E2250 There is no overloaded version of 'StrPas' that can be called with these arguments
This code is wrong on all Delphi versions. I doubt it compiles anywhere. I'm guessing that the code you presented is not the Delphi 7 code, but rather the code after you've hacked at it for a while.
The return type should be string and not char. Furthermore, the cast to WideString is bogus. Finally, a zero-based array of characters can be treated as PChar.
Your function should be translated like so:
function ThisModuleName: string;
var
p: array [0..511] of Char;
fileNamePart: PChar;
begin
GetModuleFileName(HInstance, p, Length(p));
GetFullPathName(p, Length(p), p, fileNamePart);
Result := p;
end;
Having said all of that, while this is a faithful translation of the code in the question, it does not return a module name. I really don't know what your code is trying to do, but the call to GetFullPathName appears to be wrong in your code.
My guess is that you are trying to convert potential short 8.3 file names to long names. I believe that you need an extra buffer to make that work. Here's what that code looks like, with some error checking added:
function ThisModuleName: string;
var
ModuleFileName, Buffer: array [0..511] of Char;
FilePart: PChar;
begin
Win32Check(GetModuleFileName(HInstance, ModuleFileName, Length(ModuleFileName))<>0);
Win32Check(GetFullPathName(ModuleFileName, Length(Buffer), Buffer, FilePart)<>0);
Result := Buffer;
end;
Instead of asking a question for every problem you encounter in your porting project it might pay dividends to learn a bit more about Unicode Delphi.
Instead of calling the API directly, you can call System.SysUtils.GetModuleName, which simply returns a string.
It wraps GetModuleFilename, and by doing so it also shows how to call that function. I hope I'm allowed to quote a couple of lines from the unit mentioned above. It also uses the MAX_PATH constant, which contains the maximum length of a path.
Note that GetModuleFilename already returns a fully qualified path, so calling GetFullPathName afterwards is redundant.
function GetModuleName(Module: HMODULE): string;
var
ModName: array[0..MAX_PATH] of Char;
begin
SetString(Result, ModName, GetModuleFileName(Module, ModName, Length(ModName)));
end;
This is mainly useful if you want the path of a dll, if you're interested in the main executable, you can simply use Application.ExeName.

Delphi closure and "old style" object type

Working with anonymous functions I found out that sometimes the compiler throws the following error:
E2555 Cannot capture symbol 'Self' when I try to use some field of the object.
I also noticed that this error seems to be related to the fact that a type, the method belongs to, is declared with "object" key word:
MyType = object()
field: integer;
...
end;
MyType.Method1()
begin
p := procedure
begin
// do something with field
end;
end;
However when a type is declared with "class" keyword it seems it works fine.
I know that to prevent the compiler error I can make a local copy of needed fields and use them inside the anonymous functions, but just to be sure - is "object" type cause of the compiler error and what's the reason of that?
Thanks in advance
As David properly analyzed it is because Self in your case is a value and not a reference. It cannot be moved to the internally created class - same is the case with any method arguments that are records. They also cannot be captured for the very same reason.
For arguments I usually copy them to a local variable which is being captured.
The same can be done for capturing Self in a record or object.
However if you capture it as value you get a copy and calling the closure later might have the "wrong" state because it captured a copy. To make it work similar you would have to capture a reference to Self but then for a value type you cannot guarantee that this reference is still valid when you call the closure.
You can see this in the following code:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TProc = reference to procedure;
PRecord = ^TRecord;
TRecord = object
y: Integer;
procedure Foo;
function GetProc: TProc;
end;
procedure TRecord.Foo;
begin
Writeln(y);
end;
function TRecord.GetProc: TProc;
var
this: PRecord;
begin
this := #Self;
Result :=
procedure
begin
this.Foo;
end;
end;
procedure Nested(var p: TProc);
var
r: TRecord;
begin
p := r.GetProc();
r.y := 0;
p();
r.y := 32;
p();
end;
procedure Main;
var
p: TProc;
begin
Nested(p);
p(); // <- wrong value because PRecord not valid anymore
end;
begin
Main;
end.
If you would capture TRecord it would do a local copy that it captures - you can see that it then will print 0 all the time.
Since Turbo Pascal object is long deprecated, it is reasonable for new language features not to have support for object.
There's not really any need to look much further. Since you are maintaining legacy code, I would not expect you to be introducing new language features like anonymous methods. Once you start introducing such language features, this no longer feels like legacy code maintenance and it would be reasonable to re-factor the code away from the legacy language features like object.
Having said that, I do note that the same restriction to capture applies in methods of advanced records.
type
TProc = reference to procedure;
TRecord = record
procedure Foo;
end;
procedure TRecord.Foo;
var
P: TProc;
begin
P :=
procedure
begin
Foo;
end;
end;
This fails to compile with error:
E2555 Cannot capture symbol 'Self'
Why does this code fail, even though advanced records are a fully supported modern feature?
I don't have an explanation for that and the documentation does not make it clear. A plausible explanation is that records are value types. When a local variable is captured, it is hoisted from being a stack allocated variable to a variable owned by an internally created class. That's possible for Self when Self is a reference to an instance of a class. But when Self is a value like a record, it is too late to hoist the record.
Or perhaps it is much more prosaic. Maybe the designers just implemented the most important use case (capturing Self for a class) and omitted the less widely used cases for expediency. It is frustrating that the documentation does not appear to give any rules for what can and cannot be captured.

How do I assign a pre-existing function to a TComparison<T>?

program Project55;
{$APPTYPE CONSOLE}
uses
System.Generics.Defaults;
type
TestRec<T> = record
Compare: TComparison<T>;
CompareI: IComparer<T>;
end;
var
TRI: TestRec<Integer>;
begin
TRI.CompareI:= TComparer<Integer>.Default;
TRI.Compare:= TRI.CompareI.Compare; //E2035 Not enough actual parameters
TRI.Compare:= #TRI.CompareI.Compare; //E2035 Not enough actual parameters
end.
I know I can assign the function body as an anonymous function, but why can't I assign an existing function?
Of course the following works, but that's just silly:
TRI.Compare:= function(const L,R: integer): Integer
begin
Result:= TRI.CompareI.Compare(L,R);
end;
PS. I'm using Delphi XE7, but I doubt the version matters.
Knowing that IComparer<T> is an interface with just one method that has the same signature as TComparison<T> and that anonymous methods are just interfaces with one method you can do the following.
IComparer<Integer>(TRI.Compare) := TRI.CompareI;
I am using that trick in Spring4D to avoid creating a wrapper object around a TComparison<T> to be passed as IComparer<T> because they are binary compatible.
Your attempts to perform this assignment fail because an interface method cannot be with assigned to a method reference variable. The language simply does not permit that. The types are not assignment compatible. Valid assignment sources are anonymous methods, methods of classes (instance or class) and unit scope procedures.
The tricks that can be seen in other answers all depend on in depth knowledge of the implementation details. Which means that they are subject to change. But in terms of the language, what you are attempting is not permitted.
Anonymous methods are not exactly method pointers. They are implemented as an interface with a single method "Invoke".
It is possible to extract a method pointer from an anonymous method, but as far as I know it relies on the current implementation details of anonymous method and could be subject to changes in future version of delphi. In other words, I would advise against it. This was taken verbatim from Barry Kelly's post here. (Which covers the topic more thoroughly than I do here)
procedure MethRefToMethPtr(const MethRef; var MethPtr);
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
// 3 is offset of Invoke, after QI, AddRef, Release
TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];
TMethod(MethPtr).Data := Pointer(MethRef);
end;
Based on your example, I'd propose this as an alternative
type
TestRec<T> = record
CompareI: IComparer<T>;
function Compare(const L, R : T) : Integer;
end;
[...]
function TestRec<T>.Compare(const L, R : T) : Integer;
begin
Result := CompareI.Compare(L,R);
end;
But then, it may/may not apply to your current situation.

Dealing with overloaded functions that have ambiguous parameters

Take this small example class (not my real code, but it exposes the problem):
Convert = class(TObject)
public
class function ToString(value: Double): String; overload;
class function ToString(value: TDateTime): String; overload;
end;
It compiles fine until you try to use the Double or TDateTime functions As
In:
var
d: Double;
begin
d := 99.99;
ShowMessage(Convert.ToString(d));
You will get this compile error: Ambiguous overloaded call to 'ToString'.
The problem boils down to the fact that TDateTime is a type of Double
My Question: how do You deal with this type of problem?
EDIT - I am NOT looking for a solution for the example given
I have found 3 Solutions so far:
Rename one of the 2 functions
Add a "Dummy" parameter to one of the 2 functions
Change the parameters to Var types, this has the disadvantage that I can no longer call this function with constants
are there any other solutions out there?
Overloaded methods can be very effective. However, as soon as there is a hint of ambiguity they become a liability. A good example of this are the new TStream overloads introduced in XE3. It's not hard to fall into a trap where the compiler chooses an overload that you weren't expecting. At least in your code the compiler stopped. In that sense you were lucky.
So my advice, in your situation, is to abandon overloads. Express the different input types in the method name. Yes it's a little more verbose, but you won't make any mistakes, and you code will compile!
Your posted example compiles and executes fine in XE.
In a comment you give this example instead:
ShowMessage( Convert.ToString( 99.99 )); // <- gives compiler error 2251
In this particular case the solution is to explicitly define the type( I thought):
ShowMessage( Convert.ToString( Double(99.99) )); // <- E2089, Invalid Typecast
Looking into the documentation:
This error message is issued for type casts not allowed by the rules. The following kinds of casts are allowed:
Ordinal or pointer type to another ordinal or pointer type
A character, string, array of character or pchar to a string
An ordinal, real, string or variant to a variant
A variant to an ordinal, real, string or variant
A variable reference to any type of the same size.
So, to explicitly tell the compiler to select the Double overloaded function:
ShowMessage( Convert.ToString( Double(Variant(99.99)))); // Ok
A bit convoluted perhaps. But for the other overloaded function it is simpler:
ShowMessage( Convert.ToString( EncodeDate(2013,1,5));
Update
To make this a generic solution working for all classes, consider adding class functions to resolve your ambiguous types.
Convert = Class(TObject)
...
class function AsDouble( value: Double) : Double; inline; static;
class function AsTDateTime( value: TDateTime) : TDateTime; inline; static;
end;
class function Convert.AsDouble(value: Double): Double;
begin
Result := Value;
end;
class function Convert.AsDateTime(value: TDateTime): TDateTime;
begin
Result := Value;
end;
Now you can call your overloaded class function with constants:
ShowMessage( Convert.ToString( Convert.AsDouble(99.99)));
How about collapsing it all?:
class function Convert.ToString(value: Variant): String;
begin
Result := VarToStr(Value);
end;

How do I stop this Variant memory leak?

I'm using an old script engine that's no longer supported by its creators, and having some trouble with memory leaks. It uses a function written in ASM to call from scripts into Delphi functions, and returns the result as an integer then passes that integer as an untyped parameter to another procedure that translates it into the correct type.
This works fine for most things, but when the return type of the Delphi function was Variant, it leaks memory because the variant is never getting disposed of. Does anyone know how I can take an untyped parameter containing a variant and ensure that it will be disposed of properly? This will probably involve some inline assembly.
procedure ConvertVariant(var input; var output: variant);
begin
output := variant(input);
asm
//what do I put here? Input is still held in EAX at this point.
end;
end;
EDIT: Responding to Rob Kennedy's question in comments:
AnsiString conversion works like this:
procedure VarFromString2(var s : AnsiString; var v : Variant);
begin
v := s;
s := '';
end;
procedure StringToVar(var p; var v : Variant);
begin
asm
call VarFromString2
end;
end;
That works fine and doesn't produce memory leaks. When I try to do the same thing with a variant as the input parameter, and assign the original Null on the second procedure, the memory leaks still happen.
The variants mostly contain strings--the script in question is used to generate XML--and they got there by assigning a Delphi string to a variant in the Delphi function that this script is calling. (Changing the return type of the function wouldn't work in this case.)
Have you tried the same trick as with the string, except that with a Variant, you should put UnAssigned instead of Null to free it, like you did s := ''; for the string.
And by the way, one of the only reasons I can think of that requires to explicitly free the strings, Variants, etc... is when using some ThreadVar.

Resources