Why does the SetString built-in variable cause a "Incompatible types" error in the PChar argument?

Please excuse the stupid question, but I'm confused. Consider the following method (sorry for the noisy comments, this is real code in development):

function HLanguages.GetISO639LangName(Index: Integer): string; const MaxIso639LangName = 9; { see msdn.microsoft.com/en-us/library/windows/desktop/dd373848 } var LCData: array[0..MaxIso639LangName-1] of Char; Length: Integer; begin { TODO : GetLocaleStr sucks, write proper implementation } //Result := GetLocaleStr(LocaleID[Index], LOCALE_SISO639LANGNAME, '??'); Length := GetLocaleInfo(LocaleID[Index], LOCALE_SISO639LANGNAME, @LCData, System.Length(LCData)); Win32Check(Length <> 0); SetString(Result, @LCData, Length); // "E2008 Incompatible types" here, but why? end; 

If I delete the reference operator, then the implicit translation from $X+ comes to the rescue and the method compiles. Why the compiler refuses this code using the reference operator, I do not understand.

This is Delphi XE2, and this behavior may be specific to it.


And if I add a test layout with an equivalent prototype as internal within the framework of HLanguages.GetISO639LangName , this error will magically go away:

 procedure SetString(var s: string; buffer: PChar; len: Integer); begin { test case dummy } end; 
+4
source share
4 answers

You must explicitly convert it to PChar :

 SetString(result,PChar(@LCData),Length); 

As you said, SetString() very picky about the second type of parameters. It must be either PChar or a PWideChar , or a PAnsiChar , depending on the type of string itself.

I suspect this is due to the fact that SetString() is defined as overloaded with either a string , a WideString , or AnsiString as the 1st parameter. Therefore, in order to verify the correct signature, it must have an exact correspondence of all types of parameters:

 SetString(var s: string; buf: PChar; len: integer); overload; SetString(var s: AnsiString; buf: PAnsiChar; len: integer); overload; SetString(var s: WideString; buf: PWideChar; len: integer); overload; 

Of course, all these are "intrinsics", so you will not find such a definition in system.pas, but directly any procedure like _LStrFromPCharLen() _UStrFromPCharLen() _WStrFromPWCharLen() or such.

This behavior occurs from earlier versions of Delphi and is not a regression in XE2.

+5
source

I think there is a compiler error there, because the behavior with SetString is different from the behavior with the overloaded functions that you provide. What else is the interaction with the compiler option of the Typed @ operator. I don’t know how you did it. I always turn it on, but I suspect I'm outnumbered.

Therefore, I cannot explain the strange behavior and answer the exact question that you are asking. I suspect that the only way to answer this is to look at the internals of the compiler, and very few of us can do this.

Anyway, if that helps, I think the cleanest way to pass the parameter is:

 SetString(Result, LCData, Length); 

This compiles no matter what you specify for the Typed @ operator.

+4
source

I know this does not answer a specific question about SetString, but I would like to point out that you can do the same just by writing

 Result := LCData; 

When assigned to a string, Delphi processes the static char array with a starting index of ZERO as a zero-terminated string with a maximum length. Consider the following:

 var IndexOneArray : array [ 1 .. 9 ] of char; IndexZeroArray : array [ 0 .. 8 ] of char; S : string; T : string; begin IndexOneArray := 'ABCD'#0'EFGH'; IndexZeroArray := 'ABCD'#0'EFGH'; S := IndexOneArray; T := IndexZeroArray; ShowMessage ( has ' + inttostr(length(S)) + ' chars. ' + #13'T has ' + inttostr(length(T)) + ' chars. ' ); end; 

A message appears stating that S has 9 characters and T has 4. It will also work if the zero-index array contains 9 non-zero characters. The result will be 9 characters regardless of what is in the following memory locations.

+3
source

Because LCData is a pointer to an array , not a Char . Of course, sometimes it happens that an array or record or class starts with the char -type variable, but the consequences are not what a statically typed compiler should rely on.

You need to take a pointer to a character in this array, and not to the array itself.

 SetString(Result, @LCData[Low(LCData)], Length); 
0
source

All Articles