1

I've done some research here regarding the problem given above and come up with the following code:

VarStr = array of WideChar;

function ArrayToString(const a: VarStr): UnicodeString;
begin
  if Length(a) > 0 then
    begin
      ShowMessage ('Länge des übergebenen Strings: ' + IntToStr(Length(a)));
      SetString(Result, PWideChar(@a[0]), Length(a) div 2)
    end
  else
    Result := '';
end;

ShowMessage displays the correct number of characters in a given array, but the result of the function is always an empty string.

Your ideas please?

7
  • Length(a) div 2 means that only half of the characters are used. Rather than PWideChar(@a[0]), you can perfectly well write @a[0]. The cast is needless. However, the code will produce a runtime error if the array of is length 0 and you use range checking. Which you should. So I'd write this as PWideChar(a). Commented Dec 1, 2014 at 10:43
  • what's the content of a[0] , #0? Commented Dec 1, 2014 at 10:50
  • @bummi I don't think that matters SetString doesn't look for null terminators Commented Dec 1, 2014 at 10:58
  • @DavidHeffernan mhhmm. with a[0]=#0 (or not initialized, maybe he is not aware of 0 based) you will get an empty string as described, not a shortened string. Commented Dec 1, 2014 at 11:02
  • 1
    @DavidHeffernan not going to argue, he is talking about Showmessage, just try a[0] := #0; a[1] := 'b'; a[2] := 'c'; a[3] := 'd'; Showmessage(ArrayToStringBroken(a)); Commented Dec 1, 2014 at 11:10

2 Answers 2

6

You are passing the wrong length value. You only ask for half of the characters. Fix your code like this:

function ArrayToString(const a: VarStr): string;
begin
  SetString(Result, PWideChar(a), Length(a));
end;

However, you also report that your function returns an empty string. The most likely cause for that is that you are passing invalid input to the function. Consider this program:

{$APPTYPE CONSOLE}

type
  VarStr = array of WideChar;

function ArrayToStringBroken(const a: VarStr): UnicodeString;
begin
  SetString(Result, PWideChar(@a[0]), Length(a) div 2);
end;

function ArrayToStringSetString(const a: VarStr): UnicodeString;
begin
  SetString(Result, PWideChar(a), Length(a));
end;

var
  a: VarStr;

begin
  a := VarStr.Create('a', 'b', 'c', 'd');
  Writeln(ArrayToStringBroken(a));
  Writeln(ArrayToStringSetString(a));
end.

The output is:

ab
abcd

So as well as the problem with the code in your question, you would seem to have problems with the code that is not in your question.

Perhaps when you said:

The result of the function is always an empty string.

You actually meant that no text is displayed when you pass the returned value to ShowMessage. That's a completely different thing altogether. As @bummi points out in comments, ShowMessage will truncate its input at the first null-terminator that is encountered. Use proper debugging tools to inspect the contents of variables.

Sign up to request clarification or add additional context in comments.

20 Comments

In XE7, var a: VarStr = ['a', 'b', 'c', 'd']; looks a lot better!
FWIW, that is equivalent to the older syntax a := VarStr.Create('a', 'b', 'c', 'd'); which is, again, equivalent to your code using SetLength(...); a[0] := ...;
It is easy to forget them. I hardly use them, because they do not optimize anything. It is just a little less typing. I prefer to initialize my dynarrays from a constant array and write a little routine that either copies element by element or moves to the dynarray, depending on the base type.
@RudyVelthuis I have a static generic class that inherits from TArray<T>, is also called TArray<T>, and allows me to write: arr := TArray<Integer>.Copy([1, 2, 3, 4])
I don't, but it is a good idea. But that is equally non-performant, as the values are "generated" at runtime. But how do you inherit from TArray<T> (a generic dynamic array)?
|
1
Result:= Trim(string(a));

UPDATE: As colleagues graciously pointed in comments, this is a wrong answer! It works only because internal string and dynamic array implementation are pretty similar and there is no guarantee that such code would work in the future compilator versions. The correct way to DynArray->String conversion is described in the David answer. I would not delete my answer to preserve comments, in my opinion their worth is much greater..

14 Comments

Without Trim it should work just as well. Trim also should not eat last character (it does not do that in my test example). There is something fishy with your VarStr array.
I don't see the relevance of UnicodeString and string being synonyms. The important thing you're demonstrating here is that it's OK to type-cast a dynamic array to tell the compiler it's really a string even though string and array of WideChar are not synonyms. That's not necessarily intuitive.
Type-casting a dynamic array to a string is wrong. If you are going to do that, type-cast the array to what it is really compatible with - a PWideChar, eg: Result := UnicodeString(PWideChar(a));. Either typecast requires the array to be null terminated, though. If it is not, you have to use SetString() instead (which you should he using anyway).
What Remy said, in a nutshell. Null terminated, zero-indexed static arrays of Char are (by design) compatible with string, but dynamic arrays are not. The fact the code in this answer appears to work is because the internal format of a dynamic array of Char is similar to the internal format of a string, but that's an implementation detail which should never be relied upon - indeed, the confusion about whether the cast should be wrapped in a Trim call and/or be involved in appending an explicit null terminator hints at this.
An array of Char is not a string, and is not converted to a proper string during such a type-cast. All it is doing is tricking the compiler into thinking the array's memory pointer is pointing at a string payload, but it does not. If you trace into your example, you will see that the array pointer is passed as-is to System._Write0UString(), which passes the pointer as-is to System._LStrFromUStr(), which happens to work because a dynamic array has a length field at the same memory offset that a string does, but that conversion will only work for an array of Char.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.