I'm converting an old code from Delphi 5 to XE5.
It has this piece of code:
Boolean(RecBuf[0]) := False;
RecBuf is PChar.
This works in Delphi 7, but not in XE5.
In XE5, it gives "Left side cannot assign" error.
How to implement this code in XE5?
In Delphi 7, PChar is an alias for PAnsiChar. That is, pointer to 8 bit ANSI character. Which means that RecBuf[0] has type AnsiChar. Since AnsiChar and Boolean have the same size, the cast is valid.
In Delphi XE5, PChar is an alias for PWideChar, pointer to 16 bit wide character. And so RecBuf[0] has type WideChar. Thus the cast is invalid because WideChar and Boolean have different size.
Exactly how best to fix the problem cannot be discerned from the code that you have shown here. Quite possibly you need to redeclare RecBuf. Perhaps it needs to be declared as PAnsiChar. Although one does wonder why you are casting a character to a Boolean.
Another possibility is that the reason RecBuf is declared as PChar is to allow you to use the index operator [], something that in older versions of Delphi is a special capability of pointers to character types. In modern Delphi you can use {$POINTERMATH ON} to enable that functionality for all typed pointers. So, if you did that then perhaps RecBuf should be PBoolean.
The bottom line is that whilst we can explain why the compiler complains about your code, we cannot give you a definitive solution.
Judging from RecBuf name they are used not as a pointer to string but as a pointer to buffer allocated in memory.
If my assumption correct, you may want to redeclare RecBuf as a variable of type PByte.
You can't assign cast stuff on the left, you need to cast the right side.
Instead of
Newtype(Whatever) := NewtypeConstant;
you should use
Whatever := TUnknown(constant)
In yourcase, of recbuf is an array of say, byte, then it puts false (0) in it, so it should be
Recbuf[0] := Byte(False);
Or you could, you know, just put zero in there.
Related
I took a break from porting code, and now I'm spending some more time on it again.
Problem is, I guess i'm still stuck backwards in my head (everything works fine on D6 :D).
Can anyone tell me why this simple code is not working?
if NewSig <> NewCompressionSignature then
E2015 Operator not applicable to this operand type
Here are the definitions of the above:
NewCompressionSignature: TCompressionSignature = 'DRM$IG01';
NewSig: array[0..SizeOf(NewCompressionSignature)-1] of Char;
I'm just guessing here because the type of TCompressionSignature is not given, but I can reproduce ERROR2015 if TCompressionSignature is declared as some kind of ShortString like
type
TCompressionSignature = String[8]
As you might know, Delphi is currently using Unicode as its standard internal string encoding. For backward compatibility reasons, the type ShortString and other short string types (like String[8]) were left unchanged. These strings have the same encoding like AnsiString and are composed of standard plain old 1-byte characters (AnsiChar).
NewSig on the other hand is composed of two-byte Unicode characters and can not be compared directly with an ShortString.
One solution of your problem would be to declare:
NewSig: array[0..SizeOf(NewCompressionSignature)-1] of AnsiChar;
Another solution would be be a cast to string:
if NewSig <> String(NewCompressionSignature) then ...
But I would prefer to change the array declaration if possible.
Please review the documentation short strings and about unicode - especially if you're doing io operations to ensure your input and output is read and written with the correct codepage.
Trying to move project from Delphi 2007 to Delphi XE4. What is the best way to convert String to AnsiString in Delphi XE4?
You simply assign it:
var
AnsiStr: AnsiString;
Str: string;
....
AnsiStr := Str;
The compiler will emit a warning mind you:
W1058 Implicit string cast with potential data loss from 'string' to 'AnsiString'
You can use a cast to suppress that warning:
AnsiStr := AnsiString(Str);
By default that gives no warning, although there is of course still potential for data loss. If you enable warning W1060 then the compiler says:
W1060 Explicit string cast with potential data loss from 'string' to 'AnsiString'
Of course, it's not expected that Delphi XE4 code has much place for the use of AnsiString. Unless you have a very specific interop requirement, then text is best held in the native data type, string. If you want to operate on byte arrays use TBytes or TArray<Byte>.
I'm upgrading some ancient (from 2003) Delphi code to Delphi Architect XE and I'm running into a few problems. I am getting a number of errors where there are incompatible types. These errors don't happen in Delphi 6 so I must assume that this is because things have been upgraded.
I honestly don't know what the difference between PAnsiChar and PWideChar is, but Delphi sure knows the difference and won't let me compile. If I knew what the differences were maybe I could figure out which to use or how to fix this.
The short: prior to Delphi 2009 the native string type in Delphi used to be ANSI CHAR: Each char in every string was represented as an 8 bit char. Starting with Delphi 2009 Delphi's strings became UNICODE, using the UTF-16 notation: Now the basic Char uses 16 bits of data (2 bytes), and you probably don't need to know much about the Unicode code points that are represented as two consecutive 16 bits chars.
The 8 bit chars are called "Ansi Chars". An PAnsiChar is a pointer to 8 bit chars.
The 16 bit chars are called "Wide Chars". An PWideChar is a pointer to 16 bit chars.
Delphi knows the difference and does well if it doesn't allow you to mix the two!
More info
Here's a popular link on Unicode: The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets
You can find some more information on migrating Delphi to Unicode here: New White Paper: Delphi Unicode Migration for Mere Mortals
You may also search SO for "Delphi Unicode migration".
A couple years ago, the default character type in Delphi was changed from AnsiChar (single-byte variable representing an ANSI character) to WideChar (two-byte variable representing a UTF16 character.) The char type is now an alias to WideChar instead of AnsiChar, the string type is now an alias to UnicodeString (a UTF-16 Unicode version of Delphi's traditional string type) instead of AnsiString, and the PChar type is now an alias to PWideChar instead of PAnsiChar.
The compiler can take care of a lot of the conversions itself, but there are a few issues:
If you're using string-pointer types, such as PChar, you need to make sure your pointer is pointing to the right type of data, and the compiler can't always verify this.
If you're passing strings to var parameters, the variable type needs to be exactly the same. This can be more complicated now that you've got two string types to deal with.
If you're using string as a convenient byte-array buffer for holding arbitrary data instead of a variable that holds text, that won't work as a UnicodeString. Make sure those are declared as RawByteString as a workaround.
Anyplace you're dealing with string byte lengths, for example when reading or writing to/from a TStream, make sure your code isn't assuming that a char is one byte long.
Take a look at Delphi Unicode Migration for Mere Mortals for some more tricks and advice on how to get this to work. It's not as hard as it sounds, but it's not trivial either. Good luck!
Why can't I perform this operation:
var
data:pbyte;
x:int64;
o:pointer;
begin
o:=data+x;
end;
PChar is a pointer to char, but it receives special support from the compiler to allow pointer arithmetic to make C-like string manipulations easier in Delphi. PByte is just a plain old typed pointer, and does not receive any special attention from the compiler to allow pointer arithmetic.
In Delphi 2009, a new compiler directive was introduced ($POINTERMATH ON/OFF) which allows you to add compiler support for pointer arithmetic to your own pointer type declarations.
pbyte = ^byte;
pchar = ^char;
In old Delphi versions (prior to D2009), SizeOf(char)=SizeOf(byte), i.e., 8-bit.
In D2009 and later, char is 16-bit whereas byte remains 8-bit, so that:
SizeOf(byte)=1
SizeOf(char)=2
To allow modifying pointers by e.g. adding values etc., you can use $POINTERMATH ON (available in D2009 and later, see here). The alternative is to follow the pattern:
NewPointer:= Pointer(Integer(OldPointer)+IntegerValue)
Edit1 -- Note that (as pointed out in comments to another answer), also inc() and dec() work with typed pointers; they will increment/decrement a PMyType by SizeOf(TMyType).
Edit2 -- For future-proofing your code, you should consider that SizeOf(Pointer) will probably change in future 64-bit Delphi versions, so that the relationship SizeOf(Integer)=SizeOf(Pointer) will no longer hold. To circumvent this, recent Delphi versions define the types NativeInt and NativeUInt, which are integers that have the same size as a pointer.
Those 3 types are very similar...
TArray is the generic version of TBytes.
Both can be casted to PByteArray and used as buffer for calls to Windows API. (with the same restrictions as string to Pchar).
What I would like to know: Is this behavior "by design" or "By Implementation". Or more specifically, could it break in future release?
//Edit
As stated lower...
What I really want to know is: Is this as safe to typecast TBytes(or TArray) to PByteArray as it is to typecast String to PChar as far as forward compatibility is concerned. (Or maybe AnsiString to PAnsiChar is a better exemple ^_^)
Simply put, an array of bytes is an array of bytes, and as long as the definitions of a byte and an array don't change, this won't change either. You're safe to use it that way, as long as you make sure to respect the array bounds, since casting it out of Delphi's array types nullifies your bounds checking.
EDIT: I think I see what you're asking a bit better now.
No, you shouldn't cast a dynamic array reference to a C-style array pointer. You can get away with it with strings because the compiler helps you out a little.
What you can do, though, is cast a pointer to element 0 of the dynamic array to a C-style array pointer. That will work, and won't change.
Two of those types are similar (identical in fact). The third is not.
TArray is declared as "Array of Byte", as is TBytes. You missed a further very relevant type however, TByteArray (the type referenced by PByteArray).
Being a pointer to TByteArray, PByteArray is strictly speaking a pointer to a static byte array, not a dynamic array (which the other byte array types all are). It is typed in this way in order to allow reference to offsets from that base pointer using an integer index. And note that this indexing is limited to 2^15 elements (0..32767). For arbitrary byte offsets (> 32767) from some base pointer, a PByteArray is no good:
var
b: Byte;
ab: TArray<Byte>;
pba: PByteArray;
begin
SetLength(ab, 100000);
pba := #ab; // << No cast necessary - the compiler knows (magic!)
b := pba[62767]; // << COMPILE ERROR!
end;
i.e. casting an Array of Byte or a TArray to a PByteArray is potentially going to lead to problems where the array has > 32K elements (and the pointer is passed to some code which attempts to access all elements). Casting to an untyped pointer avoids this of course (as long as the "recipient" of the pointer then handles access to the memory reference by the pointer appropriately).
BUT, none of this is likely to change in the future, it is merely a consequence of the implementation details that have long since applied in this area. The introduction of a syntactically sugared generic type declaration is a kipper rouge.