Problem with operator in a Delphi 64 bit project

I am migrating a 64-bit Delphi project, and I have a problem with a line of code that has an IN statement.

The compiler will raise this error

E2010 Incompatible types: 'Integer' and 'Int64'

I wrote this sample application to replicate a problem.

 {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; Var I : Integer; L : Array of string; begin try if I in [0, High(L)] then except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; readln; end. 

This code works fine in 32 bits, but why doesn't it compile in Delphi XE2 64 bit? How can I fix this problem?

* UPDATE *

My post seems to have caused a lot of confusion (sorry for that), just to explain that the source code that I am porting is more complex, and I just wrote this code as a sample to illustrate this problem. in the source code, the in operator is used to check whether a value (lower than 255) belongs to a group of values ​​(all minor or equal to 255), for example

 i in [0,1,3,50,60,70,80,127,High(LArray)] 
+6
source share
3 answers

This code cannot be compiled because the High function returns an 8-byte value, which is not an ordinal value. and the operator In can only be used in sets with ordinal values.

FYI, the size of the results returned by the High function differs depending on the parameter passed as an argument.

Check out this sample.

  Writeln(SizeOf(High(Byte))); Writeln(SizeOf(High(Char))); Writeln(SizeOf(High(Word))); Writeln(SizeOf(High(Integer))); Writeln(SizeOf(High(NativeInt))); Writeln(SizeOf(High(TBytes))); 

Finally, you can fix your code by outputting the result of the High function to an integer.

  if I in [0, Integer(High(L))] then 

UPDATE

Check out the additional information provided by David, and remember to be very careful when you use the in operator to verify that the values ​​in the set with the values ​​of the variables belong to. The in operator only checks the low byte of each element (32 bits in delphi).

Check out this sample.

  i:=257; Writeln( 1 in [i]); 

This returns true because the low byte of 257 is 1.

And in 64-bit Delphi, values ​​greater than 255 are removed from the set. So this code

  i:=257; Writeln( 1 in [i]); 

will return false, since it is equivalent

  Writeln( 1 in []); 
+7
source

What RRUZ says is completely correct.

To add a little more explanation, in 64-bit Delphi, dynamic array indexes can be 64 bits wide. This is clearly necessary, for example, when working with a large TBytes memory block. And therefore, the high function should return a value of a type wide enough to hold all possible indexes. So, high when applied to a dynamic array returns an Int64 value.

As soon as you start compiling 64-bit code, the in statement is not suitable for the problem you are trying to solve. Although you can use the cast that RRUZ offers, it might be clearer to write code like this

 if (I=low(L)) or (I=high(L)) then 

While the in operator does for completely readable code, I find casting to Integer here unacceptable. This will just set a trap for you when you first get an array with more than high(Integer) elements. When this happens, the throw code will stop working.

But in fact, the problems go much deeper than that. The in version of the code does not fire long before you reach the high(Integer) elements. Turns out your code, while it compiles, actually doesn't work. For example, consider this program:

 program WeirdSets; {$APPTYPE CONSOLE} uses SysUtils; var a: array of Integer; begin SetLength(a, 257); Writeln(BoolToStr(Length(a) in [0, Length(a)], True)); end. 

You expect this program to output True , but in fact it outputs False . If instead you should have written

 Writeln(BoolToStr(Length(a) in [0, 257], True)); 

then the compiler reports:

 [DCC Error] WeirdSets.dpr(9): E1012 Constant expression violates subrange bounds 

The main problem here is that the sets are limited to 256 elements, so as soon as you have an array with a longer length, your code will stop working.

Unfortunately, the Delphi support for the sets is simply inadequate and requires urgent attention.


I also wonder if you really intended to write

 if I in [0..High(L)] then 

If so, I would recommend using the InRange function from Math .

 if InRange(I, 0, High(L)) then 

or even better

 if InRange(I, low(L), High(L)) then 
+4
source

The most serious problem with the OP code is that the in operator is limited by the size of set , i.e. [0..255]. Try this on any 32-bit version of Delphi to avoid the 64-bit problem:

 var I: Integer; L: array of Integer; begin SetLength(L, 1000); I:= 999; Assert(I in [0, High(L)]); // fails ! end; 

The OP is lucky if Length(L) <= 256 always, otherwise it is an error that you probably never thought about.

To find this range of error switches, follow these steps:

 {$R+} procedure TForm1.Button2Click(Sender: TObject); var I: Integer; A: array of Integer; begin SetLength(A, 1000); I:= 999; if I in [0, High(A)] then ShowMessage('OK!'); // Project .. raised exception // class ERangeError with message 'Range check error'. end; 
+2
source

Source: https://habr.com/ru/post/903543/


All Articles