Variable Required error with TStream.Write() - delphi

I am trying to compile a project but I guess this error "Variable Required"
function ReadInteger(SomeTStream:TStream):integer;
begin
SomeTStream.Read(Result, SizeOf(Result));
end;
Top:=ReadInteger(SomeTStream);
Left:=ReadInteger(SomeTStream);
Height:=ReadInteger(SomeTStream);
Width:=ReadInteger(SomeTStream);
Then when it tries to write it stops in Top and Left.
SomeTStream.Write(Top,SizeOf(Top));
^
SomeTStream.Write(Left,SizeOf(Top));}
^
E2036 Variable required
I read about the problem in here
But still I have no idea what should I do to fix it.

It's exactly as the error says. The parameter must be a variable. But you are passing a property that is implemented using a getter function. Copy the property into a variable and pass that to the function.
var
Value: Integer;
....
Value := Top;
Stream.Write(Value, SizeOf(Value));
Some helper methods would be most useful here to avoid drowning in boiler-plate. In fact you already added one for ReadInteger and you just need a matching one for WriteInteger.
procedure WriteInteger(Stream: TStream; Value: Integer);
begin
Stream.Write(Value, SizeOf(Value));
end;
What's going on here is that the Write method of TStream is declared like so:
function Write(const Buffer; Count: Longint): Longint; virtual; abstract;
Because this function receives an untyped parameter, it cannot be passed by value. The address of the parameter is actually passed, and so the caller needs to supply something with an address. Your property does not fit the bill.
Incidentally, usually, Write and Read are the wrong methods to call on TStream. They do not perform any error checking. They leave that to you. The Write and Read are abstract methods that are used by specialized classes to provide the stream implementation. You are generally expected to call WriteBuffer and ReadBuffer.
Or perhaps a binary writer would help.
var
bw: TBinaryWriter;
....
bw := TBinaryWriter.Create(Stream);
try
bw.Write(Top);
bw.Write(Left);
// etc.
finally
bw.Free;
end;

Related

Delphi - How can I pass Generic parameter to function that accept Array of const parameter

I have a 'Base class' which contain a 'function' that accept parameter of type 'Array of const' as shown below:-
type
TBaseClass = class(TObject)
public
procedure NotifyAll(const AParams: array of const);
end;
procedure TBaseClass.NotifyAll(const AParams: array of const);
begin
// do something
end;
I have another 'Generic class' that is derived from the 'base class' ( defined above )
type
TEventMulticaster<T> = class(TBaseClass)
public
procedure Notify(AUser: T); reintroduce;
end;
procedure TEventMulticaster<T>.Notify(AUser: T);
begin
inherited NotifyAll([AUser]); ERROR HERE
end;
Every time I compile this code it gives error saying:
Bad argument type in variable type array constructor
What does it referring to be wrong?
You cannot pass a Generic argument as a variant open array parameter. The language Generics support simply does not cater for that.
What you can do instead is wrap the Generic argument in a variant type, for instance TValue. Now, you cannot pass TValue instances in a variant open array parameter either, but you can change NotifyAll() to accept an open array of TValue instead:
procedure NotifyAll(const AParams: array of TValue);
Once you have this in place, you can call it from your Generic method like so:
NotifyAll([TValue.From<T>(AUser)]);
Fundamentally, what you are attempting to do here is combine compile-time parameter variance (Generics) with run-time parameter variance. For the latter, there are various options. Variant open array parameters are one such option, but they do not play well with Generics. The alternative that I suggest here, TValue, does have good interop with Generics.
The System.Rtti unit has something exactly for you needs, but not widely known:
TValueArrayToArrayOfConst() and
ArrayOfConstToTValueArray()
So your implementation should be:
procedure TEventMulticaster<T>.Notify(AUser: T);
var
ParametersAsTValueArray: array[1 .. 1] of TValue;
begin
ParametersAsTValueArray[1] := TValue.From<T>(AUser);
NotifyAll(TValueArrayToArrayOfConst(ParametersAsTValueArray));
end;
Notes:
TValueArrayToArrayOfConst()'s result is a non-owning container. It contains memory pointers backed by the source array of the TValue container. ParametersAsTValueArray is alive and not being altered while the array of const is used.
One of these 2 Rtti procedures has a bug with regard to TClass values processing. TClass becomes a Pointer on some stage, and string.Format() breaks because Pointer and TClass are not the same thing. Perform tests on all TVarRec.VType, they are not so many, much less that Variant's VType.

Should I use a class or a method to wrap a dynamic remote procedure call?

Situation
I'd like to make an RPC interface easier to use. This is a custom interface so there is no readily available wrapper.
I have to write several wrappers around functions which often have many arguments.
Possible solutions
Solution 1 - Using a class for each function:
TDoSomethingFunction = class
public
property Arg1: Integer;
property Arg2: string;
property Arg3: Boolean;
procedure Run;
end;
The caller has to create an object to call the function:
var
DoSomething: TDoSomethingFunction;
begin
DoSomething := TDoSomethingFunction.Create;
try
DoSomething.Arg1 := 0;
...
DoSomething.Run;
finally
free;
end;
Method 2 - Using a wrapper method for each function:
procedure TRPCInterface.DoSomething(AArg1: Integer; AArg2: string; AArg3: Boolean);
The caller can simply call it:
TRPCInterface.DoSomething(0, ...);
Pro and contra
Method 1 - Class for each function
Contra
More code required.
An object must be created which takes up memory.
Pro
Reading the code is easier, you don't have to look at the declaration to see what the arguments are.
Method 2 - Wrapper method
Contra
You can't tell which arguments are used by just looking at the code.
Pro
Much less code to write.
The wrapper is thinner (no object has to be created).
Which method should I use?
There is an intermediate solution that is calling the wrapper methods passing an object argument.
TDoSomethingArgs = class
public
property Arg1: Integer;
property Arg2: string;
property Arg3: Boolean;
end;
procedure TRPCInterface.DoSomething(Args: TDoSomethingArgs);
one advantage of this method is that you still use methods, but still it's more readable.
One advantage of using classes (you can also use records) in arguments is that you can later change the arguments (add more, change behavior) and if you choose it well, it does not break backward compatibility - in summary you can change method signature without breaking code.
You haven't specified Delphi version, but if yours supports generics, I would go with:
type
TArgList = class( TDictionary< String, Variant > );
type
TBaseFunc = class
private
FArgs: TArgList;
public
function Run: Boolean; virtual; abstract;
public
property Args: TVarList read FArgs write FArgs;
end;
type
TSpecialFunc = class( TBaseFunc )
public
function Run: Boolean; override;
end;
implementation
function TSpecialFunc.Run: Boolean;
begin
// here's where you can access args as variants
end;
you can use:
ASpecialFunc.Args.AddOrSetValue('ArgumentName', 2012);
in this way you will have to write more code, but it's much more readable IMHO and easy to be picked up by other developers in the future.
NOTE: that I haven't tested this code, so chances are that it won't compile.
That's my two cents, I'm very curios as to what others come up with (:

How to overload a function based on the result type?

just a question, i have:
myclass = class
public
function Funct1: String;
function Funct2: Integer;
end;
It turn me error, so i have tried with:
myclass = class
public
function Funct1: String; overload;
function Funct2: Integer; overload;
end;
but same problem; delphi tell me that has same parameter.
Now, i ask, is possible to do in mode to have more function with same name but with different output as in example?
Thanks very much for help.
UPDATE
Sorry, i done a error, not funct1 and funct2, but both funct1, so:
myclass = class
public
function Funct1: String; overload;
function Funct1: Integer; overload;
end;
Doing so, compiler return me this error:
[DCC Error] Project1.dpr(15): E2252 Method 'funct1' with identical parameters already exists
[DCC Error] Project1.dpr(22): E2037 Declaration of 'funct1' differs from previous declaration
Of course, i know becouse give error and need change name to one of both function (for it me confused before) but i wanted know if there was some trick or other solution for to have a situation as this without error.
Thanks again.
You could turn the function into a procedure taking a var parameter:
myclass = class
public
procedure Funct1(var AResult: String); overload;
procedure Funct1(var AResult: Integer); overload;
end;
Firstly, for functions to be overloaded, they have to be named the same.
Secondly, this is not possible to do. Overloaded functions must have different parameters.
In your case, there is no way the compiler can tell which of your functions to call (assumed that both are renamed to Funct1):
var
v: Variant;
mc: myclass;
begin
v := mc.Funct1;
end;
What you post doesn't make sense. The first example should compile without any problem, as the functions have different names, Funct1 and Funct2.
Problems only arise when methods (or functions) have the same name. Then, normally, an overload directive would be in order, but overload can't distinguish functions on return value alone.
So assuming the names are the same, what you want is impossible. There is no way to overload these functions, if they don't have a different parameter signature. You can just give them different names, which is preferrable anyway, as they apparently do different things.
FWIW, your question is flawed by the fact that you apparently did not post the exact code with which you are actually having problems. Please always post the exact code that causes your problems, and if there are error messages, always post the exact error message (they can usually be copied using the usual copy keystrokes, e.g. Ctrl+C, even in most parts of the IDE or in message dialogs in Delphi). If there are any line numbers in the error message, indicate this in the source code you post, as we don't always have the same line numbers as you have.
if you want to overload methods with different return types, just add a dummy parameter with default value to allow the overload execution, but don't forget the parameter type should be different so the overload logic works e.g:
type
myclass = class
public
function Funct1(dummy: string = EmptyStr): String; overload;
function Funct1(dummy: Integer = -1): Integer; overload;
end;
use it like this
procedure tester;
var yourobject : myclass;
iValue: integer;
sValue: string;
begin
yourobject:= myclass.create;
iValue:= yourobject.Funct1(); //this will call the func with integer result
sValue:= yourobject.Funct1(); //this will call the func with string result
end;
also don't forget that this way your gonna have a problem with variants like in this example:
procedure tester;
var yourobject : myclass;
vValue: variant;
begin
yourobject:= myclass.create;
vValue:= yourobject.Funct1();
end;
until you implement the variant function too:
function Funct1(dummy: Variant = Unassigned): Variant; overload;
As was already stated, you cannot do it as you are wanting to do. However, you could just as easily implement each function with a different name such as "AsInteger" or "AsString". Your code will be clearer and this is generally the way it is done within the VCL.
TmyClass = class (TObject)
public
function AsString : string;
function AsInteger : Integer;
end;

Untyped / typeless parameters in Delphi

What type are parameters without type like in the class TStringStream:
function Read(var Buffer; Count: Longint): Longint; override;
What is the type of Buffer parameter (is it a type of Pointer ?).
I wrote an article about this very topic a few years ago:
What is an untyped parameter?
Untyped parameters are used in a few situations; the TStream.Read method you ask about most closely matches with the Move procedure I wrote about. Here's an excerpt:
procedure Move(const Source; var Dest; Count: Integer);
The Move procedure copies data from an arbitrary variable
into any other variable. It needs to accept sources and destinations of
all types, which means it cannot require any single type. The procedure
does not modify the value of the variable passed for Source, so that
parameter’s declaration uses const instead of var, which is the
more common modifier for untyped parameters.
In the case of TStream.Read, the source is the stream's contents, so you don't pass that in as a parameter, but the destination is the Buffer parameter shown in the question. You can pass any variable type you want for that parameter, but that means you need to be careful. It's your job, not the compiler's, to ensure that the contents of the stream really are a valid value for the type of parameter you provide.
Read the rest of my article for more situations where Delphi uses untyped parameters.
Check out the Delphi help for "Untyped parameters"
You can pass in any type, but you have to cast it in the implementation. Help says that you cannot pass it a numeral or untyped numeric constant. So basically you have to be know what type to expect, and compiler can not help you, so you need a good reason to do it this way. I suppose it could be of use if you need the method to handle incompatible types, but then again you could write several overloaded versions for each expected type, I would suggest that as a better solution.
Perhaps surprisingly, it is legal to pass a dereferenced pointer as an untyped parameter. And the pointer itself doesn't even have to have a type.
procedure SomeMethod(var aParameter);
∶
procedure CallSomeMethod(aIsInteger : Boolean);
type
buffer : Pointer;
intValue : Integer;
realValue : Single;
begin
if aIsInteger then
begin
buffer := #intValue;
end
else
begin
buffer := #realValue;
end;
SomeMethod(buffer^);
Of course it would probably have been easier if the parameter to SomeMethod() had been a pointer, but this might not be under your control.
var in a parameter list is the Delphi syntax for call by reference. It can be typed as e.g. the AllowChange parameter in the OnChanging handler of an Listview:
procedure TSomeForm.LVOnChanging(Sender: TObject; ...; var AllowChange: Boolean);
begin
if SomeProblemOccurred then
AllowChange := False;
end;
or untyped as in your example.

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.

Resources