Delphi compile-time integer conversion warnings? - delphi

In Delphi XE or 2006, is there any way to detect at compile time that implicit conversions between integer types may lose data? I realize it's possible to detect this with runtime checking. I would want it to flag the following example even if the "big" value were 1. (We're considering changing int to bigint for certain database keys and want to determine the impact on a large legacy codebase.)
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
small: Integer;
big: Int64;
begin
big := 3000000000000;
small := big; // Detect me!
Writeln(small);
end.

You won't get any warnings or hints at compile time. The Delphi compiler does not do any program flow analysis which tells it that big contains a too large value when it is assigned to small. It silently truncates the value to make it fit in the smaller type. I tried with Shortint, a signed byte-sized type and even that did not give a warning or hint.
And there is no way to make Delphi warn you. It would be nice. Perhaps you can suggest it in QC (if it wasn't already suggested)?

In Delphi, even in Delphi 10.3 -- no.
But take a look into software calling 'Pascal Analyzer', mfg by www.peganza.com
They have a lot of options and one of them is (taken from software Help):
Source code for test, take a look into line #32:
Analyze result show possible bad assignment in line #32:

Related

Memory layout of a set

How is a set organized in memory in Delphi?
What I try to do is casting a simple type to a set type like
var
MyNumber : Word;
ShiftState : TShiftState;
begin
MyNumber:=42;
ShiftState:=TShiftState(MyNumber);
end;
Delphi (2009) won't allow this and I don't understand why. It would make my life a lot easier in cases where I get a number where single bits encode different enum values and I just could cast it like this. Can this be done?
One approach I was going to go for is:
var
ShiftState : TShiftState;
MyNumber : Word absolute ShiftState;
begin
MyNumber:=42;
end;
But before doing this I thought I'd ask for the memory layout. It's more a feeling than knowlege what I am having right now about this.
A Delphi set is a bit field who's bits correspond to the associated values of the elements in your set. For a set of normal enumerated types the bit layout is straight-forward:
bit 0 corresponds to set element with ordinal value 0
bit 1 corresponds to set element with ordinal value 1
and so on.
Things get a bit interesting when you're dealing with a non-contiguous set or with a set that doesn't start at 0. You can do that using Delphi's subrange type (example: set of 3..7) or using enumerated types that specify the actual ordinal value for the elements:
type enum=(seven=7, eight=8, eleven=11);
EnumSet = set of enum;
In such cases Delphi will allocate the minimum required amount of bytes that will include all required bits, but will not "shift" bit values to use less space. In the EnumSet example Delphi will use two bytes:
The first byte will have it's 7th bit associated with seven
The second byte will have bit 0 associated with eight
The second byte will have bit 3 associated with eleven
You can see some tests I did over here: Delphi 2009 - Bug? Adding supposedly invalid values to a set
Tests were done using Delphi 2010, didn't repeat them for Delphi XE.
Unfortunately I just now stumbled upon the following question: Delphi 2009 - Bug? Adding supposedly invalid values to a set
The accepted answer of Cosmin contains a very detailed description of what is going on with sets in Delphi. And why I better not used my approach with absolute. Apparently a set variable can take from 1 to 32 byte of memory, depending on the enum values.
You have to choose a correctly sized ordinal type. For me (D2007) your code compiles with MyNumber: Byte:
procedure Test;
var
MyNumber: Byte;
ShiftState: TShiftState;
begin
MyNumber := 42;
ShiftState := TShiftState(MyNumber);
end;
I have used this technique in some situations and didn't encounter problems.
UPDATE
The TShiftState type has been extended since Delphi 2010 to include two new states, ssTouch and ssPen, based on the corresponding doc page (current doc page). The Delphi 2009 doc still has TShiftState defined as a set of 7 states.
So, your attempt to convert Word to TShiftState would work in Delphi 2010+, but Byte is the right size for Delphi 2009.
I use this:
For <= 8 elements, PByte(#MyNumber)^, for <= 16 elements, PWord(#MyNumber)^, etc.
If the enum takes up more space (through the min enum size compiler option) this will still work.

how to know (in code) that some characters are displayed fine (or not) in the interface of a program made in Delphi

Sorry about my english...
I'm trying to make a small program in Delphi 7.
Its interface will have text in my language, which has some characters with diacritics.
If "Language for non-Unicode programs" is set to my language those characters are always displayed fine. That's normal.
If is set to something else, sometimes are displayed fine, sometimes they are not.
How can I know that they can be displayed fine or not...?
Oh, and I can't use Unicode components, only normal.
Only way that I found is to capture the image of one characters into a bitmap and check pixel by pixel. But it's a lot of work to implement, slow and imprecise.
I can use GetSystemDefaultLangID function and know that "Language for non-Unicode programs" is set to something else but still don't know if they are displayed fine or not.
Thank you for any idea.
Welcome to the joys of AnsiStrings encoded using code-pages. You should not be using AnsiStrings at all, and you know that, but you say without explaining it that you can't use unicode controls. this seems strange to me. You should be using either:
(a) A Unicode version of Delphi (2009,2010, XE), where String=UnicodeString.
(b) If not that, at least use Proper Unicode controls, such as TNT Controls, and internally use WideString types where you need to store accented or international characters.
Your version of Delphi has String=AnsiString, and you are relying on the locale that your system is set to (as you say in your question) to select the codepage representations of accented characters, a problematic scheme. If you really can't move up from Delphi 7, at least start using WideStrings, and TNT Unicode Controls, but I must say that effort is WASTED you would be better off getting Delphi XE, and just porting to Unicode.
Your question asks "how can I know if they can be stored fine or not?" You can encode and decode using your codepage, and check if anything is replaced with a "?". The windows function WideCharToMultiByte, for example behaves like this. MBCS is a world of pain, and not worth doing, but you asked how you can find out where the floor falls out from under you, so that API will help you understand your selected encoding rule.
Use WideCharToMultiByte Function - http://msdn.microsoft.com/en-us/library/dd374130(v=vs.85).aspx and check lpUsedDefaultChar parameter.
Since this has been on my research list for a while, but didn't reach the top of that list yet, I can only help you out with a few links.
You will need to to quite a bit of experimentation :-)
When using Unicode, you can use functions ScriptGetCMap and GetGlyphIndices to test if a code point is in the font.
When not using Unicode, you can use the function GetGlyphIndices
There are few Delphi translations of these functions around. This Borland Newsgroup thread has a few hints on using GetGlyphIndices in Delphi.
Here is a search ScriptGetCMap in Delphi.
This page has a list of some interesting API calls that might help you further.
An extra handicap is that because not all fonts contain all characters, so Windows can do font substitution for you.
I'm not sure how to figure out that, but it is something you have to check for too.
Good luck :-)
procedure TForm1.Button2Click(Sender: TObject);
var
ACP: Integer;
begin
ACP := GetACP;
Caption := 'CP' + IntToStr(ACP);
if ACP = 1250 then
Caption := Caption + ' is okay for Romanian language';
end;

C-style hexadecimals in Delphi - undocumented feature?

I noticed by chance that the following code
var
I: Integer;
begin
I:= StrToInt('0xAA');
ShowMessage(IntToStr(I)); // shows 170 = $AA
end;
is OK in Delphi 2009. BTW the feature helped me to extract hexadecimal constants from C header file.
I wonder is it OK to use the feature or the feature is about to be "fixed" in future versions?
It's a feature, and you can rely on it. One of the philosophical changes that occurred in the evolution of Turbo Pascal into Delphi was the acknowledgment that Delphi lives in a C-dominated world and there was more to be gained by gracefully accepting or tolerating C-isms than ignoring them and forcing the Delphi developer to sort it out. Interop with C++ Builder as mentioned by Rob was a factor, but so was the fact that Delphi was designed first for Windows, and Windows has a lot of C language artifacts in the Windows API.
I think the term "impedance mismatch" may apply here - it was simple enough to remove the impedance mismatch between Delphi hex handling and "Rest of World", so we did.
Recall that the Delphi RTL is used by C++ Builder, too. The documentation doesn't go into detail about exactly what it means when it says StrToIntaccepts "decimal or hexadecimal notation." You can safely expect StrToInt to continue to accept C-style numbers.
Val accepts the same input, as does Read (because they all end up calling System._ValLong).

Will const parameters and typecasting work as before under Delphi 64bit?

As a general rule I have circumvented a lot of classical design traps when using pointers by taking advantage of Const (untyped) parameters rather than hard coded types. This gives me the benefit of speed when executing advanced graphical functions while leaving the technical details up to the compiler. It has also made it easy to use the same code in Delphi and Free Pascal with minimal changes.Lately however, I have begun to question this due to Embarcadero's vauge statements on the evolution of Delphi and it's upcomming safety model.
For instance, concider the following example:
Type TSomeDataProc = procedure (const aInput;var aOutput) of Object;
(* Convert 8-bit pixel to 16-bit pixel *)
Procedure TMyClass.ProcessSomeData08x565(Const aInput;var aOutput);
var r,g,b: Byte;
Begin
FPalette.ExportTriplets(Byte(aInput),r,g,b);
Word(aOutput):=(R SHR 3) SHL 11 or (G SHR 2) SHL 5 or (B SHR 3);
End;
(* Convert 16-bit pixel to 24-bit pixel *)
Procedure TMyClass.ProcessSomeData565x888(Const aInput;var aOutput);
Begin
With TRGBTriple(aOutput) do
Begin
rgbtRed:=(((word(aInput) and $F800) shr 11) shl 3);
rgbtGreen:= (((word(aInput) and $07E0) shr 5) shl 2);
rgbtBlue:= ((word(aInput) and $001f) shl 3);
end;
End;
We now have two procedures with identical declarations, but they handle the pixeldata very differently. This gives us the benefit of using a lookup table to get the correct "converter" method. This should be done in either the constructor or wherever the picture bitmap is allocated, like this:
Private
FLookup: Array[pf8bit..pf32bit,pf8bit..pf32bit] of TSomeDataProc;
Procedure TMyClass.Create;
Begin
Inherited;
FLookup[pf8bit,pf16bit]:=ProcessSomeData08x565;
FLookup[pf16bit,pf24Bit]:=ProcessSomeData565x888;
end;
Whenever we need to convert pixels we simply look up the correct method and use it. The syntax remains the same for all the procedures - so we dont have to worry about "how" each procedure operates. As far as our class is concerned, they all look the same.
Procedure TMyClass.ConvertTo(aFormat:TpixelFormat);
Begin
// Get function for the correct pixel converter
FConvertProc:=FLookup[CurrentFormat,aFormat];
//Use the pixel converter
FConvertProc(GetSourcePixelAddr(x,y),GetTargetPixelAddr(x,y));
end;
The question is: Will this kind of typecasting (e.g: Const to Byte or any defined Record type) survive under 64bit? I personally cant see why not, but Embarcadero have been sort of vague regarding the new "safety" model and pointer-use, so I find it a bit hard to safeguard my code for the future.
Since such tricks are used in the RTL, I don't see deprecating a var or const typeless parameter without a lot of code breaking.
Embarcadero tries its best to maintain as much backward compatibility as possible.
They even should include back the inline asm in the 64 bit compiler, after having first made some notification about the use of an external assembler.
And such a modification won't have anything to do with the 64 bit model, whereas the x86-64 assembler was a new piece of code to write.
So you should post this question of the official Embarcadero newsgroup, but I think you don't have to worry about this.
Note that FPC already did change the CONST parameter, though not in this case.
For the normal case, CONST is not guaranteed by reference anymore for all calling conventions, but follows whatever the respective ABI specifies. A new parameter type, CONSTREF is guaranteed to be by reference.
Like all breakage of compatibility it is the problem that in TP/Delphi CONST is always by ref, but TP/Delphi is also always x86.
Among others all STDCALL functions change, like e.g. IUnknown.Queryinterface:
http://wiki.freepascal.org/User_Changes_Trunk#IInterface.QueryInterface.2C_._AddRef_and_._Release_definitions_have_been_changed
The reason is more or less that in these cases, x86 ABI information entered the generic interface, something which is not cross-architecture compatible. So one has to guess if it is part of the language, or part of the x86 implementation of the language.
Note that IUnknown is also used on other platforms for e.g. Firefox' XPCOM
Delphi might also hit such snags, but I think they primarily will effect functions/methods with explicit calling convention requirements, because one can change the internal convention to suit needs, but one can't practically change the rest of the world ((XP)COM or existing C(++) libraries) to suit existing code in Delphi

How to also prepare for 64-bits when migrating to Delphi 2010 and Unicode

As 64 bits support is not expected in the next version it is no longer an option to wait for the possibility to migrate our existing code base to unicode and 64-bit in one go.
However it would be nice if we could already prepare our code for 64-bit when doing our unicode translation. This will minimize impact in the event it will finally appear in version 2020.
Any suggestions how to approach this without introducing to much clutter if it doesn't arrive until 2020?
There's another similar question, but I'll repeat my reply here too, to make sure as many people see this info:
First up, a disclaimer: although I work for Embarcadero. I can't speak for my employer. What I'm about to write is based on my own opinion of how a hypothetical 64-bit Delphi should work, but there may or may not be competing opinions and other foreseen or unforeseen incompatibilities and events that cause alternative design decisions to be made.
That said:
There are two integer types, NativeInt and NativeUInt, whose size will
float between 32-bit and 64-bit depending on platform. They've been
around for quite a few releases. No other integer types will change size
depending on bitness of the target.
Make sure that any place that relies on casting a pointer value to an
integer or vice versa is using NativeInt or NativeUInt for the integer
type. TComponent.Tag should be NativeInt in later versions of Delphi.
I'd suggest don't use NativeInt or NativeUInt for non-pointer-based values. Try to keep your code semantically the same between 32-bit and 64-bit. If you need 32 bits of range, use Integer; if you need 64 bits, use Int64. That way your code should run the same on both bitnesses. Only if you're casting to and from a Pointer value of some kind, such as a reference or a THandle, should you use NativeInt.
Pointer-like things should follow similar rules to pointers: object
references (obviously), but also things like HWND, THandle, etc.
Don't rely on internal details of strings and dynamic arrays, like
their header data.
Our general policy on API changes for 64-bit should be to keep the
same API between 32-bit and 64-bit where possible, even if it means that
the 64-bit API does not necessarily take advantage of the machine. For
example, TList will probably only handle MaxInt div SizeOf(Pointer)
elements, in order to keep Count, indexes etc. as Integer. Because the
Integer type won't float (i.e. change size depending on bitness), we
don't want to have ripple effects on customer code: any indexes that
round-tripped through an Integer-typed variable, or for-loop index,
would be truncated and potentially cause subtle bugs.
Where APIs are extended for 64-bit, they will most likely be done with
an extra function / method / property to access the extra data, and this
API will also be supported in 32-bit. For example, the Length() standard
routine will probably return values of type Integer for arguments of
type string or dynamic array; if one wants to deal with very large
dynamic arrays, there may be a LongLength() routine as well, whose
implementation in 32-bit is the same as Length(). Length() would throw
an exception in 64-bit if applied to a dynamic array with more than 232
elements.
Related to this, there will probably be improved error checking for
narrowing operations in the language, especially narrowing 64-bit values
to 32-bit locations. This would hit the usability of assigning the
return value of Length to locations of type Integer if Length(),
returned Int64. On the other hand, specifically for compiler-magic
functions like Length(), there may be some advantage of the magic taken,
to e.g. switch the return type based on context. But advantage can't be
similarly taken in non-magic APIs.
Dynamic arrays will probably support 64-bit indexing. Note that Java
arrays are limited to 32-bit indexing, even on 64-bit platforms.
Strings probably will be limited to 32-bit indexing. We have a hard
time coming up with realistic reasons for people wanting 4GB+ strings
that really are strings, and not just managed blobs of data, for which
dynamic arrays may serve just as well.
Perhaps a built-in assembler, but with restrictions, like not being able to freely mix with Delphi code; there are also rules around exceptions and stack frame layout that need to be followed on x64.
First, look at the places where you interact with non-delphi libraries and api-calls,
they might differ. On Win32, libraries with the stdcall calling convenstion are named like _SomeFunction#4 (#4 indicating the size of the parameters, etc). On Win64, there is only one calling convention, and the functions in a dll are no more decorated. If you import functions from dll files, you might need to adjust them.
Keep in mind, in a 64 bit exe you cannot load a 32-bit dll, so, if you depend on 3rd party dll files, you should check for a 64-bit version of those files as well.
Also, look at Integers, if you depend on their max value, for example when you let them overflow and wait for the moment that happens, it will cause trouble if the size of an integer is changed.
Also, when working with streams, and you want to serialize different data, with includes an integer, it will cause trouble, since the size of the integer changed, and your stream will be out of sync.
So, on places where you depend on the size of an integer or pointer, you will need to make adjustments. When serializing sush data, you need to keep in mind this size issue as well, as it might cause data incompatibilities between 32 and 64 bit versions.
Also, the FreePascal compiler with the Lazarus IDE already supports 64-bit. This alternative Object Pascal compiler is not 100% compatible with the Borland/Codegear/Embarcadero dialect of Pascal, so just recompiling with it for 64-bit might not be that simple, but it might help point out problems with 64-bit.
The conversion to 64bit should not be very painful. Start with being intentional about the size of an integer where it matters. Don't use "integer" instead use Int32 for integers sized at 32bits, and Int64 for integers sized at 64bits. In the last bit conversion the definition of Integer went from Int16 to Int32, so your playing it safe by specifying the exact bit depth.
If you have any inline assembly, create a pascal equivalent and create some unit tests to insure that they operate the same way. Perform some timing tests of both and see if the assembly still runs faster enough to keep. If it does, then you will want to make changes to both as they are needed.
Use NativeInt for integers that can contain casted pointers.

Resources