Part of the code in which the error:
function ReadBytes(maxLen: integer): TBytes; virtual;
...
uses sysutils, windows, SysConst, commutil;
...
function TBasicBufferedStream.ReadBytes(maxLen: Integer): TBytes;
begin
if maxLen > 0 then
begin
if maxLen <= (fBufFill - fInBufPos) then
begin
SetLength(result, maxLen);
Move(fBuf[fInBufPos], result[0], maxLen);
inc(fInBufPos, maxLen);
end
else
begin
result := Peek(maxLen);
Skip(maxLen);
end;
end
else
result := nil;
end;
In Delphi 10.2 it doesn't compile, in Delphi 7 it compiled!
What is my mistake that I do not understand?
This is just an educated guess as the provided code is sparse. The small hints the provided code gave:
uses provided in the code sample after the declaration contain
System.SysUtils, the unit where TBytes is defined
The provided uses are after the declaration, so TBytes there must
have a different source than System.SysUtils
Therefore it is likely that TBytes is not actually referring to the same type in declaration and implementation part. This can be visualized for example by hovering the mouse over a type. The tool tip will tell you what the exact type is the compiler is referring to.
I can for example reproduce your problem with two small units. TBytes is declared in System.SysUtils, but I declare another one - like it is defined in Delphi 2009 (see below) in Unit3:
unit Unit3;
interface
type
TBytes = array of Byte;
implementation
end.
When I now create a unit like the following, I'm mixing up the usage of TBytes from two different Units, that aren't compatible:
unit Unit2;
interface
uses
Unit3;
function ReadBytes(maxLen: integer): TBytes;
implementation
uses
System.SysUtils;
function ReadBytes(maxLen: integer): TBytes;
begin
//
end;
end.
The types that the tool tip will show are type Unit3.TBytes: array of Byte and type System.SysUtils.TBytes: System.Array<System.Byte>.
So in fact the signature of my function in the declaration differs from the signature in the implementation.
This can be resolved by
Inspecting if the used units are actually correct and needed, can you
get rid of the one causing ambiguity?
If not possible to solve it with the first point, it is possible
to refer explicitly to which type is meant by prefixing with the
containing unit:
function ReadBytes(maxLen: integer): Unit3.TBytes;
I looked at the history of System.SysUtils.TBytes retroactively, couldn't find it for Delphi 7, but in Delphi 2009 the definition of TBytes was following: TBytes = array of Byte;
I have changed my code example with that in mind and rephrased part of the answer.
Related
If I have a TFunc<T: class> a variable of that type will allow me to do dereference the class members directly.
Even class completion supports it.
If however I have an array of TFunc<T>'s then this does not compile.
Why not? Is this a compiler error or is there some underlying reason?
program Project33;
{$APPTYPE CONSOLE}
uses
System.SysUtils;
type
TTestObject = class(TInterfacedObject, IInterface)
procedure Test;
end;
procedure TTestObject.Test;
begin
WriteLn('Test');
end;
procedure Test;
var
A: IInterface;
TestObject: array [0..4] of TFunc<TTestObject>;
SingleObject: TFunc<TTestObject>;
i: integer;
begin
for i:= 0 to 4 do begin
a:= TTestObject.Create;
TestObject[i]:= function: TTestObject
begin
Result:= a as TTestObject;
end;
TestObject[i].Test; //<<-- does not compile
SingleObject:= TestObject[i];
SingleObject.Test; // <<-- works.
end;
end;
begin
Test; ReadLn;
end.
Pascal allows the parens to be omitted when calling a function or procedure that has no parameters. In your code SingleObject is a function, and
SingleObject.Test
is equivalent to
SingleObject().Test
There is an ambiguity here. When the parser encounters SingleObject, it has to decide whether you mean to refer to the function, or to call it. The parser resolves that ambiguity by taking account of the context. In the example above, it decides that since you used the . operator, you must have intended for the function to be called, and then have the . operator applied to the value returned from that function call.
Now, for your array example, the parser appears to have trouble disambiguating. Personally I think that is an oversight. It is perfectly possible to disambiguate, so far as I can see.
Consider this program:
type
TRec = class
procedure Foo; virtual; abstract;
end;
TFunc = function: TRec;
TFuncOfObj = function: TRec of object;
TRefFunc = reference to function: TRec;
var
ScalarFunc: TFunc;
ArrFunc: array [0..0] of TFunc;
ScalarFuncOfObj: TFuncOfObj;
ArrFuncOfObj: array [0..0] of TFuncOfObj;
ScalarRefFunc: TRefFunc;
ArrRefFunc: array [0..0] of TRefFunc;
begin
ScalarFunc.Foo; // fine
ArrFunc[0].Foo; // fine
ScalarFuncOfObj.Foo; // fine
ArrFuncOfObj[0].Foo; // fine
ScalarRefFunc.Foo; // fine
ArrRefFunc[0].Foo; // E2003 Undeclared identifier: 'Foo'
end.
As you can see, only the array of reference function suffers the problem. My suspicion is that the compiler developers missed whatever steps are needed to handle this disambiguation when they added support for reference function types.
The work around is easy enough. You need to explicitly disambiguate:
ArrRefFunc[0]().Foo;
This entire issue steps from the decision in Pascal to allow function call parens to be omitted when the function has no parameters. In my opinion, given the issues with ambiguity that arise with modern programming styles, it would be nice to go back in time and change that decision.
There is some strange code in Datasnap.DSReflect unit
TDSAdapterClassType = type of TDSAdapterClass;
TDSAdapterClass = class(TPersistent)
private
FAdapteeInstance: TObject;
public
constructor Create(AdapteeInstance: TObject); virtual;
end;
and then it is used like this
var
AdapteeInstance: TObject;
FClassRef: TPersistentClass;
Result := TDSAdapterClassType(FClassRef).Create(AdapteeInstance);
At first sight it seems just like another way of declaring class reference. But logic implies that it makes no sense to introduce such variation of language construct without adding more functionality to it. Following that logic I discovered that following declarations compile:
type
TypeOfInteger = type of Integer;
TypeOfByte = type of Byte;
TRec = record
x: integer;
end;
TypeOfTRec = type of TRec;
TBytes = array of byte;
TypeOfTBytes = type of TBytes;
Interesting enough, following declaration cannot be compiled.
type
TypeOfString = type of String;
So the question is what type of actually represents and how can it be used in real life application, besides being some kind of alias for class of
Note: type of does not compile in Delphi 7, it seems that it is introduced later on, it is definitively there in XE, but I don't have Delphi 2007-2010 installed to try it there.
Update: I have filled bug report https://quality.embarcadero.com/browse/RSP-9850
In Delphi.Net we have the following definitions in SysUtils:
type
TInterfaceRef = type of interface;
function Supports(const Instance: TObject; const IID: TInterfaceRef): Boolean; overload; inline;
So it was some kind of replacement for class of that can be used for interface types.
The following document mentions a "Type reference syntax (type of Interface)":
http://edn.embarcadero.com/article/29780
Here's some more info:
http://hallvards.blogspot.de/2004/11/object-to-interface-casts.html
It seems to be related to PTypeInfo based in the TypeKind as you can write this:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TIntType = type of Integer;
TInt64Type = type of Int64;
var
intType: TIntType;
int64Type: TInt64Type;
begin
try
intType := Integer;
Assert(Pointer(intType) = TypeInfo(Integer));
intType := Cardinal;
Assert(Pointer(intType) = TypeInfo(Cardinal));
intType := NativeInt;
Assert(Pointer(intType) = TypeInfo(NativeInt));
int64Type := Int64;
Assert(Pointer(int64Type) = TypeInfo(Int64));
int64Type := UInt64;
Assert(Pointer(int64Type) = TypeInfo(UInt64));
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
But it does not work properly with all types and throws internal compiler errors for some.
It's not documented. The behaviour is non-repeatable. Some behaviour feels like class of but we don't need another way to do that. And class of for a value type is nonsensical.
My conclusion is that this must be a compiler bug. The code is invalid and should be rejected by the compiler. The bug is that the code is accepted instead of being rejected.
As can be seen from Hallvard Vassbotn's article, type of is a feature of the Delphi .net compiler that creates types that map to .net's System.RuntimeTypeHandle type. Loosely speaking therefore, type of provides for functionality equivalent to the C# typeof operator.
My best guess is that the Delphi desktop compiler accepts type of when it should not, as a vestige of the .net compiler.
I'm becoming crazy.
I'm a beginner and I want to make my first DLL.
I've followed this guide:
http://www.tutorialspoint.com/dll/dll_delphi_example.htm
I want to set a text information about a program version and read it when I want, so display it to the user through main application. This is just an example to keep confidence with DLLs, I already know that there are many other way to achieve this.
Now I'm trying to read the variable "versione" from a DLL like this:
library Clientdll;
uses SysUtils, Classes, Dialogs;
{$R *.res}
function Versione(var messaggio, versione: String):string; export; stdcall;
begin
versione:='Nessun dato ricavato. Valore di chiamata alla DLL errato!';
if messaggio='chiama' then versione:='v4.0.0 build 31';
end;
exports versione;
begin
end.
In the main application I've write this:
[...]
implementation
uses unit2;
{$R *.dfm}
function Versione(var messaggio, versione:string):string; stdcall; external 'Clientdll.dll'
[...]
Now I said 'OK, I've just to call the DLL and that's all...'. So:
procedure TForm1.Button1Click(Sender: TObject);
var x, y:string;
begin
x:='chiama';
Versione(x,y);
showmessage(y);
end;
I can read v4.0.0 build 31 in the dialog box, but when I press OK I've receive this error:
"Invalid Pointer Operation".
Any ideas?
I've try to google it, but my english is poor and some answers are difficult to understand, also with translation tools!
Don't use String as the parameter type. This is clearly explained in the comment that the IDE generates when you use File->New->Other->Delphi Projects->DLL Wizard to create a new DLL:
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
In addition, using Delphi strings means that your DLL functions are not callable from other languages such as C.
You should also expect the calling application to provide you with the memory in which to place the results (and a length parameter that tells you how large that memory buffer is, too).
Here's a minimal (quite useless) example of a Delphi dll with a single function, along with a test application that calls it. (The DLL is, as I said, quite meaningless. Any actual DLL should be designed to put the functional code in its own unit and not in the project file.)
The sample DLL source:
library SimpleTest;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes;
{$R *.res}
// Parameters:
// arg: Argument that indicates whether this is a test or
// something else, so we know which value to return
// Buffer: The space in which to place the result
// Len: The length of the buffer provided
function TestDLL(const arg: PChar; const Buffer: PChar;
const Len: Integer): Boolean; stdcall;
begin
// Make sure we use the Len parameter, so we don't overflow
// the memory we were given. StrLCopy will copy a maximum of
// Len characters, even if the length of the string provided
// as the 'source' parameter is longer.
if arg = 'Test' then
StrLCopy(Buffer, 'Test result', Len)
else
StrLCopy(Buffer, 'Non-test result', Len);
Result := True;
end;
exports
TestDll;
begin
end.
The form for the test application that calls it:
unit DLLTestForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
function TestDLL(const arg: PChar; const Buffer: PChar; const Len: Integer): Boolean; stdcall;
external 'SimpleTest.dll';
procedure TForm4.Button1Click(Sender: TObject);
var
Parm1: String;
Parm2: String;
BuffLen: Integer;
begin
Parm1 := 'Test';
// Length of buffer (including null terminator) for DLL call
// Chosen arbitrarily - I know the DLL won't return more than 15 + the
// null. I'm pretending I don't, though, and allowing extra space. The
// DLL won't return more than 30 characters, even if it has more to say,
// because it uses StrLCopy to limit the result to Len characters.
BuffLen := 30;
// Allocate space for return value
SetLength(Parm2, BuffLen);
// Call the DLL with `Test` arg
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
// Call the DLL with a different parameter value
Parm1 := 'Other';
if TestDLL(PChar(Parm1), PChar(Parm2), BuffLen) then
ShowMessage(Parm2);
end;
end.
I'm try to port some code from D2007 to DXE2.
This simplified code compiles fine in D2007. In DXE2 it show this error:
[DCC Warning] Unit1.pas(10): W1050 WideChar reduced to byte char in set expressions. Consider using 'CharInSet' function in 'SysUtils' unit.
[DCC Error] Unit1.pas(37): E2010 Incompatible types: 'AnsiChar' and 'Char'
Propably a unicode issue.
Can someone tell me why this happen and how I should correct it ?
Regards
The code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TSetOfChar = Set of Char; // Line 10
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
FCharacterSet: TSetOfChar;
public
property CharacterSet: TSetOfChar read FCharacterSet write FCharacterSet;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
CharacterSet: TSetOfChar;
j: Integer;
s: String;
begin
CharacterSet := [];
s := 'I''m just testing åäö';
for j := 1 to Length(s) do
Include(CharacterSet, s[j]); // <- Line 37
end;
end.
EDIT: Note that I am using Delphi 2007 that have no generics. I want code that still works in D2007 because there is a lot of code to port to Unicode. This is slow process.
When everything is ported, verified it works with XE2 then we can use XE2 things like generics. In the meantime we maintain the D2007 as usual and we want to avoid making a XE2 branch in the revision control system.
This is standard Unicode Delphi migration fodder. Required reading is Marco Cantù's paper
White Paper: Delphi and Unicode. If you haven't read that, do so. If you haven't read it recently, do so again.
The reason that set of char produces a warning is that the base type for sets cannot have more than 256 values. But since char is now UTF-16, that's a lot more than 256. All this means that your code can never work with sets and UTF-16 characters.
You could use set of AnsiChar and AnsiString. But if you want this code to work on Unicode data then you'll need to use something other than a set. For example TList<char> could be used.
var
CharacterSet: TList<char>;
s: string;
c: char;
.....
CharacterSet := TList<char>.Create;
s := 'I''m just testing åäö';
for c in s do
if not CharacterSet.Contains(c) then
CharacterSet.Add(c);
I would not recommend that for production. Its performance characteristics will be terrible. A hash based dictionary would do better. Best of all would be a dedicated large set class.
One final point. Characters are not the same as code points in UTF-16 which is a variable length encoding. The code in question and this answer make no allowance for that.
Is the order in which parameters are calculated before a procedure is called defined in Delphi?
IOW, if I have this ugly code (found something like this in a legacy application) ...
function A(var err: integer): integer;
begin
err := 42;
Result := 17;
end;
Test(A(err), err);
... is Test guaranteed to receive parameters (17, 42) or could it also be (17, undefined)?
Edit:
Although David's example returns different result with 32-bit and 64-bit compiler, this (luckily) doesn't affect my legacy code because Test(A(err), err) only stores an address of 'err' in the register and it doesn't matter whether the compiler does this before calling A(err) or after.
The order of parameter evaluation in Delphi is not defined.
As an interesting demonstration of this, the following program has different output depending on whether you target 32 or 64 bit code:
program ParameterEvaluationOrder;
{$APPTYPE CONSOLE}
uses
SysUtils;
function SideEffect(A: Integer): Integer;
begin
Writeln(A);
Result := A;
end;
procedure Test(A, B: Integer);
begin
end;
begin
Test(SideEffect(1), SideEffect(2));
Readln;
end.
Edited:
it seems that the compiler may violate the behavior described in the help:
From Calling Conventions help topic (emphasis mine):
The register and pascal conventions pass parameters from left to right; that is, the left most parameter is evaluated and passed first and the rightmost parameter is evaluated and passed last.