Pointer x Reference to a Class in Delphi - delphi

Is there any advantage in using Pointer instead Reference to classes?
for example:
TMyClass = class(TObject);
procedure Method;
var
pmc : ^TMyClass;
//instead of
mc : TMyClass;
begin
pmc := #ObjectMyClass;
//instead of
mc := ObjectMyClass;
if pmc.Enabled then
blablabla;
//instead of
if mc.Enabled then
blebleble;
end;

All classes in Delphi are passed/used by reference, so adding another level of indirection doesn't add anything.
This does not hold for records, which are always passed/used by value. One can use these by reference by declaring a pointer type and pass these to functions by using the pointer type or the var keyword (which handles the pointer function argument mess for you).
(Ctrl+F for "class")
http://docwiki.embarcadero.com/RADStudio/XE3/en/Pointers_and_Pointer_Types
In case you're familiar with C/C++, ^TMyClass is equivalent to:
**MyClass
This is useful in some cases like when you want to edit a pointer using a function by passing a pointer to the pointer to that function, but I doubt these constructions are possible using Delphi.

Related

Using ToString for Variant variables

The following code produces an EVariantInvalidOpError exception:
var
i : Variant;
begin
i := 10;
ShowMessage(i.ToString());
end;
All the following works good but I don't understand why the ToString function raises exception for Variant type variables:
var
i : Variant;
begin
i := 10;
ShowMessage(VarToStr(i));
end;
var
i : Integer;
begin
i := 10;
ShowMessage(i.ToString());
end;
Variants let you store values of various types in them, while the type may be unknown at compile-time. You can write an integer value into single variable of Variant type an later overwrite it with string value. Along with the value variant records stores also the type information in it. Among those values some of them are automatically allocated and/or reference counted. The compiler does a lot of stuff behind the scenes when writing or reading the value from Variant variable.
Variants of type varDispatch get even more special treat from the compiler. varDispatch indicates that the value is of type IDispatch (usually, but not necessarily related to Windows COM technology). Instance of IDispatch provides information about its methods and properties via GetTypeInfoCount and GetTypeInfo methods. You can use its GetIDsOfNames method to query the information by name.
Let's answer the question from your comment first:
Why does Delphi allow me to use the ToString function even if there is no helper implementing such function for the Variant type?
This is how Delphi implements concept called late binding. It allows you to call methods of an object which type is unknown at compile-time. The prerequisite for this to work is that the underlying variant type supports late binding. Delphi has built-in support for late binding of varDispatch and varUnknown variants as can be seen in procedure DispInvokeCore in unit System.Variants.
I don't understand why the ToString function raises exception for Variant type variables.
As discussed above, in run-time your program tries to invoke ToString method on variant value which in your case is of type varByte. Since it doesn't support late binding (as well as further ordinal variant types) you get the exception.
To convert variant value to string use VarToStr.
Here's a simple example of using late binding with Microsoft Speech API:
uses
Winapi.ActiveX,
System.Win.ComObj;
var
Voice: Variant;
begin
CoInitialize(nil);
try
Voice := CreateOleObject('SAPI.SpVoice');
Voice.Speak('Hello, World!');
finally
CoUninitialize;
end;
end.

Making a linked-list with generics

I have read how to make a pointer to a normal class and use it inside the class difinition:
type
PExample = ^TExample;
TExample = class
data: Integer;
next: PExample;
end;
but how do you do it with templatized parameters? This does not compile with the error Undeclared identifier: 'TExample' on the second line:
type
PExample = ^TExample;
TExample<T> = class
data: T;
next: PExample;
end;
Changing it to
PExample = ^TExample<T>;
does not fix it.
As you are using a class, you don't need to use a PExample. Classes are already reference types.
TExample<T> = class
data: T;
next: TExample<T>;
end;
This should work without the need to declare any pointer types. Pointer types are only required if you work with records (which are value types).
EDIT:
To my surprise I just noticed that this compiles and works in Delphi XE:
program Project;
{$APPTYPE CONSOLE}
type
TNode<T> = record
Next: ^TNode<T>;
Data: T;
end;
var
Node1, Node2: TNode<Integer>;
begin
Node1.Next := #Node2;
Node1.Data := 1;
Node2.Next := nil;
Node2.Data := 2;
WriteLn(Node1.Data);
WriteLn(Node1.Next.Data);
end.
It does still not solve the problem of defining a general generic pointer type, because this:
PNode<T> = ^TNode<T>;
does not work.
The other answers tell you how to build a generic linked list of classes. If you ever need to build a generic linked list of records, you cannot do so at present:
type
PNode<T> = ^TNode<T>;
TNode<T> = record
public
Next: PNode;
end;
does not compile.
Nor does:
type
TNode<T> = record
type
PNode = ^TNode;
public
Next: PNode;
end;
I believe that the reason for this is that the single-pass compiler does not support forward declarations of methods. This is actually more of a practical problem for operator overloading than for generics because classes can be used for generic linked lists.
Is it necessary to use a pointer to a class? Using a class reference for your "next" field should work, like this:
type
TExample<T> = class
data: T;
next: TExample<T>;
end;
The "next" field can still be NIL or can be assigned another TExample instance. This seems to negate the need of using a traditional ^ pointer (although you could argue that a reference to a class is also a pointer, just with different syntax).

Delphi trouble: Sorting a Tobjectlist<>

I want to sort my generic tobjectlist using the built-in sort method.
here is what I do:
//create the list object
myList := TObjectList<MyType>.Create(false);
[...] //populate the list with unsorted entries
//sort the list
myList.sort(#Comparer);
[...]//store sorted results back to array
myList.Destroy;
my Comparer function looks like this:
function Comparer(Item1, Item2 : pointer):integer;
begin
result := myCompare(item1, item2);
end;
According to the specs, it should work like this.
I get an compiler error E2250 No overloaded version of 'Sort' exist with these parameters (exact wording differs, i use a non english version of RAD Studio)
I have no idea why this should not be valid Pascal - does anyone of you have insight to share on this?
You are almost there. Since I don't know what MyType is you may need to change the call to your myCompare function.
myList.Sort(TComparer<MyType>.Construct(
function (const L, R: MyType): integer
begin
result := myCompare(L, R);
end
));
TObjectList<T>.Sort is declared as:
procedure Sort(const AComparer: IComparer<T>);
IComparer<T> is defined as:
IComparer<T> = interface
function Compare(const Left, Right: T): Integer;
end;
You are instantiating TObjectList<MyType> and so you need to pass an IComparer<MyType> to Sort. In order to do this you will need an object to provide a concrete implementation of that interface.
One obvious way to do this would be to subclass TObjectList<MyType> and implement the interface there.
Another way to do this is to use TComparer<T> to create an IComparer<T> on demand using its Construct class function. You would need to supply a comparison function:
TComparison<T> = reference to function(const Left, Right: T): Integer;
Leonardo's answer demonstrates how to do this.
If the compiler says no overloaded version exists with that parameter type, ask yourself what overloads do exist. Check the source code or the documentation to find out.
There you'll see that TObjectList<T> inherits two Sort methods from TList<T>. One takes no arguments, and the other takes a reference to something implementing the IComparer<T> interface. Your standalone function doesn't fit that. Write a descendant of TComparer<MyType> and override its Compare method.

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.

Arrays as result type and input for functions

in delphi7 i have a function that i need to return a array as a result type b
"function createsbox(key:tkey):array[0..255] of byte;" this is not allowed it is expecting a "identifyer expected but array found" is the error throw up. seems to work fine if i declare a record type of array but it seems pointless to do this for one function.
The issue is that you're not allowed to create a new type in a function declaration. But that's what you're doing when you specify the return type as array[0..255] of Byte. Instead, declare a named type, and then use it for the return type:
type
TSBox = array[0..255] of Byte;
function CreateSBox(const Key: TKey): TSBox;
There is a subtle reason for that, and it is in Pascal two array type declarations are not the same "type" regardless of how identical their declaration are, and thereby not assignment compatible. If you write:
var
A: array[1..10] of Integer;
B: array[1..10] of Integer;
A and B are different types. If you write
A := B;
The code won't compile, A and B are different types.
Thereby, if you write
var
A: array[1..10] of Integer;
...
function Foo(...): array[1..10] of Integer;
You're actually declaring a type for the function result - that type would be pretty useless because you could not assign it to A or any array no matter how it s declaration is, for example:
A := Foo(...);
would not work even if the compiler would let you to declare a function that way.
The only way to have a useful function result type thereby is to use a type already declared. Only open arrays are an exception to this rule, but they can be used only as function parameters, not as the result.
okay type TSBox = array of Byte works fine, but using this new type in 2 or more units, could be tricky. 'Cause u will get an error message "incompatible types".
There's a partial solution for this situation. U can inform the compiler which unit has that type with this notation: unit.type;
Example:
Imagine there I have a unit called Web, where I declare the type TDownload. I could do something like this:
var fileUrl : Web.TDownload;
In this case the compiler will understand that u r using the same type.
However, and thats my question for u all: what if u don't want to put that unit in the uses clausule for whatever - circular reference, poo good practices, etc.. What can I do to avoid this problem?

Resources