Warning on comparing a SmallInt with result of Ord function - delphi

I'm comparing a SmallInt variable with the result of the Ord function.
Example:
var
MySmallInt : SmallInt;
begin
MySmallInt := 5;
if(MySmallInt > Ord('C'))
then ShowMessage('True')
else ShowMessage('False');
end
After doing this, the following warning message is shown (W1023):
W1023 Comparing signed and unsigned types - widened both operands
Delphi's hint on the Ord function says that it should return a SmallInt and that's why I can't understand what causes the warning message. (I've looked for Ord function in the System unit but I didn't find it).
Further Informations:
I'm testing under Delphi XE7.
Under Delphi 2007, the same code doesn't give me any warning.

As David said, Ord() is a so called "compiler magic" (or, as they call it now, "intrinsic" or "pseudo-") function, i.e. not a real function that is called, but just something that uses a function syntax, but is recognized by the compiler as a special construct and turned into code directly. The same is true for e.g. Chr(), Writeln(), etc. They can usually have different and/or multiple types of parameters or return values and sometimes even have additional syntax elements.
The documentation says, about Ord(X):
The result is the ordinal position of X; its type is the smallest standard integer type that can hold all values of X's type.
In Delphi XE7, 'C' is a WideChar, and the return value of Ord('C') will be a 16 bit unsigned type (Word). Smallint is signed type. That is why you get the warning, because you are comparing a signed and an unsigned type of the same size, so the values must be widened to the next larger type (Integer).
In Delphi 2007, 'C' is not a WideChar, it is an AnsiChar, so the result of Ord('C') is a Byte. There is no need for widening to the next larger type, since Smallint can contain all values of Byte, so both can be promoted to Smallint.
I agree that the info hint in the editor is deceptive. Ord() does not always return a Smallint, it returns the minimum type that is needed to hold all values of the argument.

Ord() is an intrinsic function that yields an unsigned type. Hence the warning. In your case, you are passing it a WideChar, and so the matching integral type is Word.

Related

Why can I divide Single by Variant in Delphi, and what is it for?

I was surprised to see, that floating-point values could be divided by a Variant in Delphi. A simple example of what can be done:
var
v: Variant;
begin
v := 2.3;
Tag := 5.1 div v; // 2
Tag := 5.1 mod v; // 1
Tag := 5.1 div 2; // [dcc32 Error] E2015 Operator not applicable to this operand type
Tag := 5.1 mod 2; // [dcc32 Error] E2015 Operator not applicable to this operand type
end;
It looks like Delphi rounds the left-part and right-part before doing the div/mod operation.
I would expect above code to produce errors at compile-time in all 4 lines, since my understanding is that div/mod are not applicable to floating-point values no matter what. Clearly this is not the case.
Why can I divide Single by Variant in Delphi, and what is it needed for?
From Variants in Expressions:
If an expression combines variants with statically-typed values, the statically-typed values are automatically converted to variants.
This means that the float typed literal is converted to a variant first.
Then both variants are implicitly converted to integer values to match the operator before doing the div/mod operation.
Variant Type Conversions
..and what is it needed for?
Just to be as versatile as possible and fully support OLE.

Error compiling HtmlViewer component for Delphi 7

I am trying the compile the HtmlViewer component for Delphi 7 (https://github.com/BerndGabriel/HtmlViewer). Opened the project Frameviewer7.dpk under package subdir
However I am getting the following compilation errors:
HtmlBuffer.pas(1611): Array Type required.
Which corresponds to the following code:
if FStart.BytePtr[0] = 0
And FStart is defined as FStart: TBuffPointer;
TBuffPointer = record
case Integer of
0: (BytePtr: PByte;);
1: (WordPtr: PWord;);
2: (AnsiChr: PAnsiChar;);
3: (WideChr: PWideChar;);
end;
Not sure what is wrong here. My compiler is Delphi7
FStart.BytePtr[0] indicates that FStart.BytePtr is an array, and the value of this expression is the first (0th) element in this array.
However, FStart.BytePtr is actually a pointer. But often you can use arrays and pointers to achieve the same task -- either you use an array of TSomeType, or you use a pointer to the first element in an in-memory list of TSomeType items.
I assume this is what is going on here. Hence, you want to get the first item of a list of byte values, the first occurring at address FStart.BytePtr. To obtain the byte at this location, you dereference the pointer using ^: FStart.BytePtr^.
The code you have found tries to access data using array notation on a pointer. This syntactic sugar might work in some newer version or Delphi, or using some compiler option. (I don't recall.)
This syntax uses a feature of later Delphi compilers that allows you to use indexed references for offsets from typed pointers. In some versions of Delphi this requires the POINTERMATH compiler option or directive to be specified.
Unfortunately this is not supported in Delphi 7.
The typical way to work around this is to use an array type and declare a pointer to this array type. The actual bounds of the array type are not important (in the sense that you will be using the pointer type so you will not be creating actual large array structures, only treating pointers as if they were references to such structures).
The only consideration is that the upper limit needs to be higher than or equal to the highest index you will require to specify in order to keep the compiler happy that any literal indexes you specify may be valid.
i.e. if you specified an array of only 100 items then any code that attempted to reference a 101st item would fail bounds checking either at compile time or runtime (if runtime checks are enabled).
So for a simple example we'll use an array of 65535 items:
const
MAX_BYTEARRAYDIM = 65535;
type
TByteArray = array[0..MAX_BYTEARRAYDIM] of Byte;
PByteArray = ^TByteArray;
procedure SomeExampleMethod;
var
pb: PByteArray;
begin
// ..
pb[12] := 25; // The array type is 0 based so this sets the value of byte offset 12 bytes from the address in pb
end;
This has the advantage (should it be a concern in your code) of being portable to all versions of Delphi.
Application in Your Case
In your specific case you could redefined the BytePtr type in this way. Not being familiar with the HTMLViewer code I cannot say whether this may be practical.
An alternative would be to declare the necessary array and pointer types and to to typecast as and where required, e.g.:
if PByteArray(FStart.BytePtr)[0] = 0
Of course, the same technique can be applied to other pointer types as required.

Convert hex str to decimal value in delphi

I've got a problem to convert a string representation of an hex value in integer value with Delphi.
for example:
$FC75B6A9D025CB16 give me 802829546 when i use the function:
Abs(StrToInt64('$FC75B6A9D025CB16'))
but if i use the calc program from Windows, the result is: 18191647110290852630
So my question is: who's right? me, or the calc?
Does anybody already have this kind of problem?
In fact 802829546 is clearly wrong here.
Calc returns a 64bit unsigned value (18191647110290852630d).
Delphi Int64 type uses highest bit as sign:
Int := StrToInt64('$FC75B6A9D025CB16');
Showmessage(IntToStr(Int));
returns value -255096963418698986 which is correct
If you need to work with values larger than 64bit signed, then check out Arnaud's answer here.
The number is too big to be represented as a signed 64-bit number.
FC75B6A9D025CB16h = 18191647110290852630d
The largest possible signed 64-bit value is
2^63 - 1 = 9223372036854775807
to work with big numbers you need external library for delphi
Large numbers in Pascal (Delphi)
I had to use a Delphi library named "DFF Library" because I work on Delphi6 and the type Uint64 does not exist in this version.
Main page
Here's my code to transform a string of hexadecimal value to a string of decimal value:
You need to add UBigIntsV3 to your uses in your unit.
function StrHexaToUInt64Str(const stringHexadecimal: String): string;
var
unBigInteger:TInteger;
begin
unBigInteger:=TInteger.Create;
try
// stringHexadecimal parameter is passed without the '$' symbol
// ex: stringHexadecimal:='FFAA0256' and not '$FFAA0256'
unBigInteger.AssignHex(stringHexadecimal);
//the boolean value determine if we want to add the thousand separator or not.
Result:=unBigInteger.converttoDecimalString(false);
finally
unBigInteger.free;
end;
end;

How to define an unsigned 64-bit integer in Delphi7?

In Delphi 7, int64s are signed, if I try to declare a hex constant larger than $8000000000000000 (eg, what is really an uint64) I get an error. Can you advise some workarounds, please?
You can make a variant record like so
type muint64 = record
case boolean of
true: (i64 : int64);
false:(lo32, hi32: cardinal);
end;
Now you can just use the cardinals to fill your uint64 with unsigned data.
The other option would be to use code like this:
const almostmaxint64 = $800000045000000;
var muint64: int64;
begin
muint64:= almostmaxint64;
muint64:= muint64 shl 1;
end
Without support from the compiler you don't have many options.
I'm presuming that you wish to pass a value to a function in some external DLL. You'll have to declare the parameter as a signed 64 bit integer, Int64. Then all you can do is pass in the signed value that has the same bit pattern as the desired unsigned value. Build yourself a little converter tool with a compiler that has support for unsigned 64 bit integers.
Traditionally, Broland implementations suffered interoperability issues because lack of largest unsigned supported by target platform. I remember using LongInt values instead of DWORD and waiting for troubles since very early days of Turbo Pascal for Windows. Then was Cardinal happiness, but no, D4 introduced largest integer Int64 in its signed form only. Again.
So your only option is to rely on signed fundamental type Int64 and pray... wait, no, just use Int64Rec typecast to perform arithmetics on least and most significant part separately.
Back to constant declaration:
const
foo = $8000004200000001; // this will work because hexadecimal notation is unsigned by its nature
// however, declared symbol foo becomes signed Int64 value
// attempting to use decimal numeral will result in "Integer constant too large" error
// see "True constants" topic in D7 Help for more details
procedure TForm1.FormCreate(Sender: TObject);
begin
// just to verify
Caption := IntToHex(foo, SizeOf(Int64) * 2);
end;
Unfortunately, the other workaround is to change your compiler. Free Pascal always keeps signed and unsigned types in sync.
This snippet compiles and yields correct result in Borland Delphi Version 15.0 (a.k.a Delphi 7).

Enumerated Type: Limit to number of items?

Is there a limit in Delphi to the number of items you can have in an enumerated type? I need to create an enumerated type that might have several hundred items, and want to make sure there is not a limit at 255 items for example.
type
TMyType = (mtOne, mtTwo, mtThree, ..., mtThreeHundred);
I believe the theoretical limit is 2^32 items; but in practice, RTTI generation is normally the limit, as RTTI can't exceed 65535 bytes to store everything, including the names of the enumeration elements; the names are stored in UTF-8, so it's not too bad.
On the other hand, enumerations with explicit values for the elements don't have full RTTI, so you can evade the limit that way. Here's a program which creates a source file with 500,001 enumeration elements, which itself compiles:
var
i: Integer;
begin
Writeln('type');
Writeln(' E = (');
for i := 1 to 500000 do
Writeln(' x_', i, ' = ', i, ',');
Writeln('x_last);');
Writeln('begin');
Writeln('end.');
end.
The output of this program takes some time to compile with dcc32 because the Delphi compiler uses a hash table with only 32 buckets for checking for enumeration identifier duplicates, and a hash table with only 256 buckets for file-level scope, which (in the absence of {$SCOPEDENUMS ON}) is where enumeration identifiers are added.
I found a maximum of 65535 items in a german Delphi book.
After some digging in the documenation I found the respective section:
Enumerated Types
An enumerated type is stored as an
unsigned byte if the enumeration has
no more than 256 values and the type
was declared in the {$Z1} state (the
default). If an enumerated type has
more than 256 values, or if the type
was declared in the {$Z2} state, it is
stored as an unsigned word. If an
enumerated type is declared in the
{$Z4} state, it is stored as an
unsigned double-word.
So in fact there should be a possible maximum of 4294967295 ($FFFFFFFF) items.
Try it and see? It should just take a few minutes to write a loop that will build your type statement as long as you want. Output with a messagebox (which can be copied to the clipboard with ctrl+c), paste back into Delphi, and you're all set.
Yes enums in Delphi can have more than 256 items. You won't have problem with them, but if you are going to use set types, you should take note that sets can have 256 elements at most.

Resources