Howto cast pointer to generic parameter type? - delphi

it's my first question here, glad to have found this site.
My question deals with the new Generics feature in Delphi 2009. Basically I tried to write a generic wrapper class for an existing hash map implementation. The existing implementation stores (String, Pointer) pairs, so in the wrapper class I have to cast between the generic parameter type T and the Pointer type and vice versa.
type THashMap <T : class> = class
private
FHashList : THashList;
...
end;
I thought of a cast like this (Value : T)
Value := (TObject (Ptr)) as T
But this doesn't work. The compiler tells me 'Operator not applicable to this operand type'.
Somebody has some hints? Thanks a lot in advance.

You need to take the address of the location of the generic type parameter type, then typecast this address to a pointer to the desired type, and then dereference this pointer and assign into the resulting location. For example:
PObject(#Value)^ := Ptr;
The reason you can't just typecast a value of type T, where T is unconstrained, is that the compiler doesn't know the size of T; normally, non-numeric typecasts can only convert values into types that are of the same size.
Unfortunately, the compiler is not smart enough to figure out that a class-type constraint means that T is guaranteed to be the same size as a pointer.
Also, there is an issue with current Delphi 2009 generics with creating pointers to type parameter types. Generic pointers are not supported by the compiler, but the compiler permits this syntax inside classes:
type
C<T> = class
type
PT = ^T; // UNSUPPORTED!
end;
This may work for certain scenarios - and can be helpful for your specific problem - but it only works by accident and is not generally supported. Use at your own risk.

Try this:
Value := TObject (Ptr)
No need to cast more, as assigning the TObject to the generic class type variable is valid :)
But I do not know the reason why you cannot use T for casting in the first place...

Related

Is there any way to dynamically cast the item type of a generics collection in Delphi?

Unlike the case with common objects, it is impossible to directly assign generics of different related types in Delphi as follows:
Possible (normal objects):
var
var_1 : TObject;
var_2 : MyTObjectSubClass;
var_1 := var_2; //Works
Not possible (generics):
var
var_1 : TList<TObject>;
var_2 : TList<MyTObjectSubClass>;
var_1 := var_2; //Does not compile
It is possible to use casts to accomplish this though, as follows:
var
var_1 : TList<TObject>;
var_2 : TList<MyTObjectSubClass>;
var_1 := TList<TObject>(var_2); //Works
This creates the need to be able to dynamically cast generics (i.e. to dynamically parameterize their generic type specification) somehow, but I have not been able to find a way to do this, so my question is: Is this in any way possible?
I'm indeed aware of the covariance/contravariance problems related to this, but in some cases it would indeed both be useful and "correct" to do such a thing.
One example of such a situation is the current code I'm writing for generic streaming of Delphi objects over a TStream, where the receiving end knows the exact type of the object that is incoming over the stream, e.g. TList<MyTObjectSubClass>. This type information is extracted by means of RTTI though (from a provided target variable to which the loaded object should be written), so I cannot explicitly mention the exact generics type in my stream-loading code in advance, but rather have to detect it by means of RTTI (which is possible, although somewhat hacky) and then write it to a target variable that I only at that run-time point will know the exact type of.
Thus, the load-object-from-stream code has to be fully generic, and thus, it would need to dynamically cast an existing TList<TObject> variable (which is defined explicitly in the code) to the exact type of TList<MyTObjectSubClass> (which I at that point have just learned about, through the use of RTTI), in order to be able to pass this object loaded from the stream to its final destination variable.
So again, is there ANY way whatsoever to accomplish this, or is it on the contrary actually completely impossible to assign to a not-in-advance-known generics collections using generic code (i.e. code that does not explicitly have some kind of "if [type of xxx is TList<TMyObject1>] then ... else if [type of xxx is TList<TMyObject2>] then ... else ..." test, containing explicit mentions of every single generics type that should be supported by it)?
PS.
The generics type of the stream-loaded object obviously already exists somewhere in the program (since it is concluded by means of RTTI on the target variable that the stream-loaded object should be written to), so I'm not asking about full run-time dynamic creation of generics types, but rather just about how to be able to dynamically pick the right one of those generics types already defined at compile-time in the program, and then cast a variable to that type.
EDIT:
By request from #RemyLebeau , here comes some more example code from my application, from its stream-loading function:
var
source_stream : TStream;
field_to_process : TRttiField;
field_type : TRttiType;
loaded_value : TValue;
temp_int : integer;
//...
//The fields of any object given to the streaming function are
//enumerated and sorted here
//...
//Then, for each field (provided in field_to_process),
//the following is done:
case field_to_process.FieldType.TypeKind of
//...
tkInteger:
begin
source_stream.ReadBufferData(temp_int);
loaded_value := TValue.From(temp_int);
end;
tkString,
tkLString,
tkWString,
tkUString:
begin
source_stream.ReadBufferData(noof_raw_bytes_in_string_data);
SetLength(raw_byte_buf, noof_raw_bytes_in_string_data + 4);
source_stream.ReadBuffer(raw_byte_buf[0], noof_raw_bytes_in_string_data);
temp_str := used_string_encoding.GetString(raw_byte_buf, 0, noof_raw_bytes_in_string_data);
loaded_value := TValue.From(temp_str);
end;
tkClass:
begin
is_generics_collection_containing_TObject_descendants := <does some hacky detection here>; //Thanks Remy :-)
if is_generics_collection_containing_TObject_descendants then
begin
<magic code goes here that loads data from the stream into the currently processed field, whose type has been detected to be of some specific generics collection type>
end;
end;
//...
end;
field_to_process.SetValue(self, loaded_value);
That should hopefully give a somewhat better overview of my problem. The superfluous code for strings and integers are just for context, by showing how some simple types are handled.
For more info about the (necessarily) "hacky detection" mentioned in the code, please see this question. After doing that, I will know the exact type of the generics collection and its subitems, for example TList<TSomeTObjectDescendant>.
So, as you hopefully can see now, the question is about the <magic code goes here that loads data from the stream into the currently processed field, whose type has been detected to be of some specific generics collection type> part. How can it be implemented?
NOTE: My problem is not to understand how to serialize/deserialize contents of an enumerable through a stream (which can of course be done by simply iterating over the items in the enumerable and then recursing the stream saving/loading code for each of them, where the number of items is given first of all in the stream). The problem is rather how to create generic code that will be able to recreate/populate any kind of generics collection of TObject descentants, whose type you only get to know at runtime, and then to finally get this into the object field that was originally enumerated by RTTI at the beginning of the stream-loading code. As an example, assume that the processed field has the type TList<TSomeTObjectDescendant>, and that you can easily load its subobjects from the stream using a call like function load_list_TSomeTObjectDescendant_subitems(input_stream : TStream) : array of TSomeTObjectDescendant. How could I then get these subitems into the TList<TSomeTObjectDescendant> field?
Type-casts and variable declarations are parsed at compile-time (though is and as casts are executed at runtime based on compiler-provided RTTI). The type being casted to, and the type of the variable being assigned to, must be known to the compiler. So what you are asking for is simply not possible with Generics. Not the way you have described it, anyway.

Where is the definition of "TypeIdentifier"

I would need to write a function that performs operations in RTTI mode on data, of which I do not know a priori the type.
I tried to write a function like this:
function doSomething (T: TypeIdentifier): Boolean;
as when using the expression TypeInfo(T) he expects as a type parameter TypeIdentifier
But when I try to compile I get an error because the type TypeIdentifier not recognized.
Someone could explain me how I can do to send a data type of which I know the type only at runtime?
Thanks to the availability.
Enzo
TypeInfo is an intrinsic function that uses compiler magic. That is the compiler generates the code that implements the function rather than it being part of the runtime library.
You don't have access to such magic. You cannot create a function that accepts a type identifier in the manner of TypeInfo. So you need to make your function accept what TypeInfo returns, a pointer to the type info, PTypeInfo.
You'd call your function like this:
DoSomething(TypeInfo(SomeTypeIdentifier));
Now technically, you may notice that TypeInfo returns a value of type Pointer. That's because PTypeInfo is defined in another unit, TypInfo and the System unit where all intrinsics are defined is not allowed to use TypInfo. But as stated in the documentation linked above, TypeInfo returns a pointer to TTypeInfo.

Delphi, is possible have a property and function with the same name?

consider this code
type
TMyObject=class
private
FName: Integer;
function Name: Integer;
public
property Name : Integer read FName;
function Name(Param1 : integer) : Integer; overload;
end;
Is possible in delphi create a property and a function (or procedure) with the same name?
Exist any directive or compiler switch which allow create a class like that?
No, it is not. In addition, there is no directive or compiler switch that would allow this.
The answer is in the error message that you got when you tried this...you did try it right?
Since i see the overload keyword. I suspect that perhaps what you need default are parameters
If you use
function Name(Param1: Integer = SOME_VALUE): Integer;
it can be called as either :=Name or :=Name(5)
By accident I discovered it is possible to have a class with a property that has the same name as a function in the parent class (or vice versa).
However, I would avoid this because it will confuse you. Especially if the function and property have different meanings!
Currently, the compiler cannot do what you want.
In theory, a future version of the compiler could:
The signature of a method (overloaded or not) consists of the name and the parameter types.
The same holds for the signature of indexed properties.
Since the signature spaces of properties and methods are partially linked (hence the compiler error message), that combined space could be extended to include property overloads.
Of course that extension can backfire because of backward compatibility.
--jeroen

Delphi: determine actual type of a generic?

Is there any way to determine the type of a variable passed as an argument to a method? Consider the class:
TSomeClass = class
procedure AddToList<T: TDataType; U: TListClass<T>>(Element: T; List: U);
end;
with the method implementation
procedure TSomeClass.AddToList<T, U>(Element: T; List: U);
begin
if Element is TInt then
List.AddElement(TInt.Create(XXX))
else if Element is TString then
List.AddElement(TString.Create(YYY));
end;
where TInt.Create() and TString.Create() have different sets of arguments, yet, they both inherit from TDataType.
Now, I know the is-operator can't be used like this, but is there a legal alternative that does what I'm asking here?
Not being able to use the is operator here is a known issue, but there's a pretty simple workaround.
if TObject(Element) is TInt then
List.AddElement(TInt.Create(XXX))
Also, since the type of a generic is part of the class and is known at compile-time, you might be better off restructuring your code. Make two different generic classes, one of which accepts a TInt as its <T> parameter, and the other of which accepts a TString. Put the type-specific functionality into them at that level, and have them descend from a common ancestor for shared functionality.
This question I asked some time ago
Conditional behaviour based on concrete type for generic class
might be of interest, especially if you want to use not only TObject descendants but also primitive types in your conditionals.

how to safely bypass Delphi Error: "types of formal and actual parameters must be identical"

I need a way to write a generic procedure to act upon an object type or any of its descendants.
My first attempt was to declare
procedure TotalDestroy(var obj:TMyObject);
but when using it with a descendant object
type TMyNewerObject = class(TMyObject);
var someNewerObject: TMyNewerObject;
TotalDestroy(someNewerObject);
I get the infamous error "types of formal and actual parameters must be identical"
So, while strugling to find a solution, I looked at the source code of Delphi system FreeAndNil procedure. And I found this awesome declaration, along with this astonishing comment
{ FreeAndNil frees the given TObject instance and
sets the variable reference to nil.
Be careful to only pass TObjects to this routine. }
procedure FreeAndNil(var Obj);
It avoids the type checking error, but it uses no safety net.
My question is ... is there any safe way to check the type of an untyped var parameter?
or in other words, can you improve this Delphi source code so that the warning would not be needed?
procedure FreeAndNil(var Obj);
var
Temp: TObject;
begin
Temp := TObject(Obj);
Pointer(Obj) := nil;
Temp.Free;
end;
Let's examine what you want to do.
You want to call a method that takes X, passing in an object of type Y, where Y is a descendant of X. The snag, the parameter is a "var" parameter.
Let's analyze what you could do if that was possible.
type
TBase = class
end;
TDescendant = class(TBase)
end;
procedure Fiddle(var x: TBase);
begin
x := TDescendant.Create;
end;
type
TOtherDescendant = class(TBase)
end;
var a: TOtherDescendant;
a := TOtherDescendant.Create;
Fiddle(a);
Uh-oh, now a no longer contains an instance of TOtherDescendant, it contains an instance of TDescendant. That probably comes as a surprise to the code that follows the call.
You must not only consider what you intend to do with the syntax you propose, but effectively what you could do with the syntax.
You should read Eric Lipperts excellent blog post about similar issues in .NET, found here: Why do ref and out parameters not allow type variation?.
I've written about this before, using an example very similar to Lasse's:
Delphi Q&A: Why must the types of actual and formal var parameters be identical?
Unless you're writing an assignment statement to change the value of the input parameter itself, and not just one of its properties, you shouldn't pass a parameter by reference in the first place.
If you are writing an assignment statement to change the parameter's value, then the compiler message really is true, and you should heed it.
One reason for needing to by-pass the error is when you're writing a function like TApplication.CreateForm. Its job is to change the input parameter's value, and the type of the new value varies and cannot be determined at compile time. If you're writing such a function, then your only option with Delphi is to use an untyped var parameter, and then there is extra burden on both the caller and the receiver to make sure everything goes right. The caller needs to make sure it passes a variable that is capable of holding values of whatever type the function will put in it, and the function needs to make sure it stores a value of a type compatible with what the caller requested.
In the case of CreateForm, the caller passes in a class-reference literal and a variable of that class type. The function instantiates the class and stores the reference in the variable.
I don't think very highly of either CreateForm or FreeAndNil, largely because of the way their untyped parameters sacrifice type safety in return for comparatively little extra convenience. You haven't shown the implementation of your TotalDestroy function, but I suspect its var parameter will ultimately provide the same low utility as in those other two functions. See my articles on both:
When should I use FreeAndNil?
Why shouldn't I call Application.CreateForm?
In addition to what Lasse wrote, which is quite correct, most of the time you don't want to pass an object to a var parameter anyway.
An object is a reference type. What you see as the object is actually a reference to it. You would only want to pass an object reference to a var parameter if you wanted to change your object out for a new object. If you just want to be able to modify the members of the object, then you can do that by simply passing it to a normal parameter. Make method call take a TMyObject parameter instead of a var TMyObject parameter and it should work.
Of course, if you really are replacing the object, then feel free to disregard all this, and see Lasse's answer.
can you improve this Delphi source code so that the warning would not be needed?
Yes, you can get a type safe way to avoid the compiler error.
In the newest Delphi 10.4 Sidney, the FreeAndNil procedure has been changed into this:
procedure FreeAndNil(const [ref] Obj: TObject);
var
Temp: TObject;
begin
Temp := Obj;
TObject(Pointer(#Obj)^) := nil;
Temp.Free;
end;
It is type safe for objects and will catch errors when passing an interface reference for example.
The way to pass a parameter by const [ref] means that the parameter is passed by reference. Without the [ref] attribute, parameters with size equal and smaller than a pointer would otherwise be passed by value.
Here, even though the object is passed as a constant, the reference will be modified.
In that sense, it is not a perfect declaration, but will do its job better than the former implementation.
From New features in Delphi 10.4:
This means that incorrect usage of FreeAndNil will now cause a compiler error. In the past, incorrect usage would not be caught, leading to difficult bugs. Note that although the parameter is declared as const, the by-reference variable is indeed modified.
A new, but ‘not as bad’, class of incorrect calling is possible with this declaration of FreeAndNil: the method can be called passing in properties or a method result, as well as cast expressions, a type’s implicit conversion to TObject, etc. The nil-ed value will then be the temporary variable in the expression.

Resources