Dynamical assignment of values in Delphi - delphi

I am creating a control similar to object inspector , So i want to assign any changes to the property to the relevant object.
var
v:TValue ;
ctx : TRttiContext;
begin
// k.IsOrdinal := true ;
v := v.FromVariant(2) ;
ctx.GetType(tButton).GetProperty('Style').SetValue(Button1, v.AsOrdinal);
end;
above is my code , but i am getting invalid type cast error.
Is it possible to handle any variable and enums .(No need of objects and records as it is very complicated )

The call to SetValue needs to read like this:
SetValue(Button1, TValue.From(TButton.TButtonStyle(2)))
In your code, the use of AsOrdinal is incorrect. That is a function that returns a TRttiOrdinalType. But TRttiOrdinalType is described thus:
TRttiOrdinalType is the class used to describe all the Delphi ordinal value types, such as Integer, Byte, Word, and so on.
But you need to provide a TValue that represents a TButtonStyle, which is what the code above achieves.
As an aside, I initially tried to use the generic TValue.From<T>() function like this:
SetValue(Button1, TValue.From<TButton.TButtonStyle>(TButton.TButtonStyle(2)));
But that just resulted in the following internal compiler error:
[DCC Fatal Error] Unit58.pas(38): F2084 Internal Error: URW1147
QC#103129
Every time I attempt to use generics I end up being defeated by these internal errors!
Thanks to Serg for pointing out the alternative form of calling the parameterised method using type inference does not fall foul of the internal error.

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.

Generic function with interface constraint [duplicate]

I just tried my first use of generics in Delphi 2009 and am perplexed on how to use a generic type as the input to the Supports function used to see if an object implements a given interface. I've created a small sample illustrating the problem.
Given the following types and utility function:
IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;
TMyObject = class(TInterfacedObject, IMyInterface)
end;
class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
specificInterface: T;
begin
// This would compile, but looses the generic capability
//Supports(myObject, IMyInterface, specificInterface);
// This results in compile errors
Supports(myObject, T, specificInterface);
result := specificInterface;
end;
and the following code snippet:
class procedure TFunctions.Test;
var
myObject: TMyObject;
myInterface: IMyInterface;
begin
myObject := TMyObject.Create;
myInterface := GetInterface<IMyInterface>(myObject);
end;
I would expect no problems but I get the following compile time errors:
[DCC Error] GenericExample.pas(37): E2029 '(' expected but ',' found
[DCC Error] GenericExample.pas(37): E2014 Statement expected, but expression of type 'T' found
I'm not sure what the compiler is expecting me to do with the T when used as the actual argument to the function.
I've searched around quite a bit and haven't been able to crack this one. A part of me suspects that if I could understand how an interface name gets converted to the IID: TGUID type during compilation, when using a concrete interface name, I could make some headway, but that has evaded me also.
Any help is much appreciated.
There is no guarantee that T has a GUID associated with it, and there is no means in the language to write a constraint on the type parameter to make that guarantee.
The interface name is converted into a GUID by the compiler looking up the name in the symbol table, getting the compiler's data structure representing the interface, and checking the corresponding field for the GUID. But generics are not like C++ templates; they need to be compiled and type-checked and known to work for any valid type parameter, and that means constraining the type parameter in its declaration.
You can get the GUID using RTTI (first checking that T does indeed represent an interface) with something like GetTypeData(TypeInfo(T))^.Guid and pass the GUID to Supports that way.
Why are you even bothering?
To use this TFunctions.GetInterface you need:
an interface
an object reference
If you have those, then you can just call Supports() directly:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
is exactly equivalent to:
Supports(IMyInterface, myObject, intf);
Using generics here is a waste of time and effort and really begs the question "Why Do It?".
It is just making things harder to read (as is so often the case with generics) and is more cumbersome to use.
Supports() returns a convenient boolean to indicate success/failure, which you have to test for separately using your wrapper:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
if Assigned(intf) then
// ...
versus:
if Supports(IMyInterface, myObject, intf) then
// We can use intf
When creating wrappers around functionality it is generally the case that the result is an improvement in readabilty or usability.
imho this fails on both counts and you should just stick with the Supports() function itself.

Using SmartPointer as result data type in function requires invoke call explicitly

I am using the SmartPointer in http://members.adug.org.au/2011/12/05/smart-pointers/
I defined a IStringList:
type
IStringList = ISmartPtr<TStringList>;
I may then use as follow without memory leak prompt:
var S: IStringList;
begin
S := TSmartPtr<TStringList>.Create();
S.Add('abc');
end;
If I use the IStringList as result data type in a function:
function GetList: IStringList;
begin
Result := TSmartPtr<TStringList>.Create();
Result.Add('abc'); // E2010
end;
I get a compiler error:
[dcc32 Error] Unit2.pas(31): E2010 Incompatible types: 'SmartPointer.ISmartPtr<System.Classes.TStringList>' and 'Procedure of object'
A workaround solution would be:
Result.Invoke.Add('abc');
But that defeat the purpose of syntax cleanliness of using SmartPointer. Is there a solution?
It's quite an interesting one. For whatever reason, the compiler treats Result differently from other variables. I see no good reason for that, so this feels like a compiler bug.
I see a few workarounds:
Declare a local variable, use that, and finish the function by assigning that local variable to Result.
Using parens to call the function: Result().Add('abc').
Using a better smart pointer. For instance, a smart pointer that is not based on method invocation would be a good start. A generic record with an implicit conversion operator would presumably work.
Find a version of the compiler without the bug, if indeed it is a compiler bug.
Give up on smart pointers and write traditional style code. Every time I contemplate using smart pointers in Delphi I conclude that I'd rather see the explicit try/finally blocks and understand when my objects are created and destroyed.
FWIW, you can greatly simplify the issue by stripping out the smart pointer code, and removing the generic types. Here is the cleanest SSCCE that I can concoct:
{$APPTYPE CONSOLE}
type
TFunc = reference to function: TObject;
procedure Foo;
var
F: TFunc;
begin
F.ClassName; // compiles
end;
function Bar: TFunc;
begin
Result().ClassName; // compiles
Result.ClassName; // [dcc32 Error] E2003 Undeclared identifier: 'ClassName'
end;
begin
end.
I'm now convinced that this is a compiler bug.
It is presumably related to the rather unusual language feature of Delphi that means the function call parentheses can be omitted for a function that has no parameters. This convenience sometimes brings with it ambiguity. However, in this case there is no ambiguity. The . operator has no meaning when applied to a TFunc<TObject> and so the only way to interpret these statements is that the function is called, and the . operator applied to the returned value.
Bug report: QC123218.
{ I'm not allowed to comment... -.-' Feel free making it one. }
I'm afraid there is no way without defeating the purpose of smart pointers. I tried to solve your problem as part of trying to find a way to assign an anonymous method to an interface variable or parameter (SO question from July 2013). Coming back to it every now and then or asking around didn't help in finding a solution.

Use of Supports() function with generic interface type

I just tried my first use of generics in Delphi 2009 and am perplexed on how to use a generic type as the input to the Supports function used to see if an object implements a given interface. I've created a small sample illustrating the problem.
Given the following types and utility function:
IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;
TMyObject = class(TInterfacedObject, IMyInterface)
end;
class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
specificInterface: T;
begin
// This would compile, but looses the generic capability
//Supports(myObject, IMyInterface, specificInterface);
// This results in compile errors
Supports(myObject, T, specificInterface);
result := specificInterface;
end;
and the following code snippet:
class procedure TFunctions.Test;
var
myObject: TMyObject;
myInterface: IMyInterface;
begin
myObject := TMyObject.Create;
myInterface := GetInterface<IMyInterface>(myObject);
end;
I would expect no problems but I get the following compile time errors:
[DCC Error] GenericExample.pas(37): E2029 '(' expected but ',' found
[DCC Error] GenericExample.pas(37): E2014 Statement expected, but expression of type 'T' found
I'm not sure what the compiler is expecting me to do with the T when used as the actual argument to the function.
I've searched around quite a bit and haven't been able to crack this one. A part of me suspects that if I could understand how an interface name gets converted to the IID: TGUID type during compilation, when using a concrete interface name, I could make some headway, but that has evaded me also.
Any help is much appreciated.
There is no guarantee that T has a GUID associated with it, and there is no means in the language to write a constraint on the type parameter to make that guarantee.
The interface name is converted into a GUID by the compiler looking up the name in the symbol table, getting the compiler's data structure representing the interface, and checking the corresponding field for the GUID. But generics are not like C++ templates; they need to be compiled and type-checked and known to work for any valid type parameter, and that means constraining the type parameter in its declaration.
You can get the GUID using RTTI (first checking that T does indeed represent an interface) with something like GetTypeData(TypeInfo(T))^.Guid and pass the GUID to Supports that way.
Why are you even bothering?
To use this TFunctions.GetInterface you need:
an interface
an object reference
If you have those, then you can just call Supports() directly:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
is exactly equivalent to:
Supports(IMyInterface, myObject, intf);
Using generics here is a waste of time and effort and really begs the question "Why Do It?".
It is just making things harder to read (as is so often the case with generics) and is more cumbersome to use.
Supports() returns a convenient boolean to indicate success/failure, which you have to test for separately using your wrapper:
intf := TFunctions.GetInterface<IMyInterface>(myObject);
if Assigned(intf) then
// ...
versus:
if Supports(IMyInterface, myObject, intf) then
// We can use intf
When creating wrappers around functionality it is generally the case that the result is an improvement in readabilty or usability.
imho this fails on both counts and you should just stick with the Supports() function itself.

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