tags:

views:

105

answers:

2

What is the difference between

this:

SourceString := 'I am doing just fine!';     
MemoryStream.ReadBuffer(Pointer(SourceString)^, xxx);

(full source code available here: http://edn.embarcadero.com/article/26416)

and this code (mine):

SetLength(SourceString, xxx);
MemoryStream.ReadBuffer(SourceString[1], xxx);  
  1. Do I really have to use Pointer(SourceString)^ or SourceString[1] is ok also?
  2. The code (both of them) will work with Delphi 2010 (unicode)?
+2  A: 

1: The SourceString[1] version is more readable. I prefer not to work with pointers when they aren't completely necessary.

2: This code will not work with Unicode. You'll have to multiply it: xxx * sizeof(Char). (This will work with both pre- and post-Unicode versions of Delphi.) But unless you're making heavy use of non-Ansi chars, this will be a big waste of space. What I prefer to do is:

procedure TStreamEx.WriteString(const data: string);
var
  len: cardinal;
  oString: UTF8String;
begin
  oString := UTF8String(data);
  len := length(oString);
  self.WriteBuffer(len, 4);
  if len > 0 then
    self.WriteBuffer(oString[1], len);
end;

procedure TStreamEx.ReadString(const data: string);
var
  len: cardinal;
  iString: UTF8String;
begin
  self.ReadBuffer(len, 4);
  if len > 0 then
  begin
    SetLength(iString, len);
    self.ReadBuffer(iString[1], len);
    result := string(iString);
  end
  else result := '';
end;

(This is part of a class helper for TStream I wrote that makes it a lot easier to read and write various things to and from streams. But if you don't like class helpers, it shouldn't be too hard to adapt the basic idea to a different format.)

Mason Wheeler
"This is part of a class helper for TStream" - My code is also from a class helper of my own :) Though I was pretty sure it will not work with Delphi 2010 unless I change the string to AnsiString (which I just did recently). Though, I am not really interested in saving UNICOE, I admit, I will update my code (using yours) to support UNICODE. Thanks.
Altar
SourceString[1] version is more readable - Thanks for confirmation Mason. I have found the code on Borland's web site and I thought "it must be something with it, if they are giving it as example". What's the point of doing it more complicated that it should?
Altar
When you have range checking enabled (and you should always have range checking enabled), then `SourceString[1]` will raise an exception when the length is zero, whereas `Pointer(SourceString)` will be a null pointer, and that won't get dereferenced since the byte count you pass in is also zero. See *Listing 4* at [my Web site](http://www.cs.wisc.edu/~rkennedy/string-stream).
Rob Kennedy
Mason, class helpers can be very good, but beware if you are using them, because if you will use a third party library for instance that also uses a helper for TStream one will not work. As you probably know only one class helper can be used for one class. I would advise against them for this alone. They are great if you use them on you own internal classes, but not for TStream which is a part of Delphi itself.
Runner
@Rob - that's a good article
Altar
+1  A: 

In the generated asm code:

  • pointer(aString)^ will pass the string address directly to the procedure/function/method;
  • aString[1] will call UniqueString then pass the string address to the procedure/function/method.

So pointer(aString)^ is to be used if you're about to read the data, not modify it.

And aString[1] is to be used if you're about to modify aString in the called function.

In practice, I use pointer(aString)^ which produces more efficient code.

Note that this implicit UniqueString is not so slow: if the current reference count of the string is 1 (which means that there is only one part of your code using the string, which is very likely), it returns immediately. But there is a LOCK asm prefix in the UniqueString used to check the reference count value, and use of this LOCK asm is not multi-thread friendly. That's why I try to avoid using aString[1] when I'm coding.

Additional note: if aString is '', pointer(aString) will return nil.

A.Bouchez
In other words, pointer(aString)^ is faster while aString[1] is safer?
Altar
you can say that :) but in some cases, the implicit call to UniqueString is mandatory to avoid memory corruption: you can use an explicit UniqueString call before using pointer(aString)^ if you want your code to be both safe and fast
A.Bouchez