As I said in my last post, while I haven’t come across a code sample in the help as bad as the action manager one, I do find a slight air of dubiousness amongst the code examples generally. I don’t think this is a new phenomenon mind – the D7 help is in places just as bad. Neatly, the case of the example code for StrUpper and StrUpper illustrates this.
So, here’s what it looked like in the D7 help:
uses SysUtils; const S: PChar = 'A fUnNy StRiNg' begin Canvas.TextOut(5, 10, string(StrLower(S)) + ' ' + string(StrUpper(S))); end;
Horrible or what! Not only does it fail to compile due to a missing semi-colon, but even when that’s fixed, it becomes apparent that the code’s author doesn’t seem to realise that the StrXXX routines work ‘in place’, unlike their native string equivalents. What he or she seemed to be thinking of was this:
const S: string = 'A fUnNy StRiNg'; begin Caption := LowerCase(S) + ' ' + UpperCase(S); end;
Naturally, this works fine, since LowerCase and UpperCase don’t attempt to modify what is passed to them.
So, what do we have in more recent Delphi versions? The following, to be exact (I’ve copied and pasted from the D2009 CHM, but the D2010 docwiki version is pretty much identical at my time of writing):
{ The following example uses an edit box, a label, and a button on a form. When the button is clicked, the text in the edit control is displayed in lower case in the label’s caption. } var S: array[0..20] of Char = 'A fUnNy StRiNg'; procedure TForm1.Button1Click(Sender: TObject); var lower, upper : string; begin lower := string(StrLower(S)); upper := string(StrUpper(S)); Canvas.TextOut(5, 10, lower + ' ' + upper); end; procedure TForm1.FormCreate(Sender: TObject); begin Label1.Caption := S; end;
Note how the author has obviously taken the D7 version as a base. Nevertheless, he or she has gone on to create a version that not just compiles, but runs without creating an access violation. Well done! And yet: ‘When the button is clicked, the text in the edit control is displayed in lower case in the label’s caption.’ Oh really… Moreover, while there is a possibility that the author realises the TextOut call only works because the string casts created a copy of the source data, I wouldn’t be surprised if he or she just got lucky. Cf. this:
var S: array[0..20] of Char = 'A fUnNy StRiNg'; procedure TForm1.FormCreate(Sender: TObject); var lower, upper : PChar; begin lower := StrLower(S); upper := StrUpper(S); Caption := lower + ' ' + upper; end;
By simplifying the surrounding code a bit, the nature of the StrXXX functions is made clear, and the ‘example’ is rendered broken. (If you don’t immediately see this, just try it out.)
Of course, the question might then get thrown back at me of what should a code sample for something like StrLower look like. To that, my first thought is that there need not be example code here at all — use of PChar buffers is not really a beginner topic, so if you need a code sample for something as simple as StrLower, then you really shouldn’t be coding at the level of PChar buffers in the first place. Nonetheless, how about this:
program Project1; {$APPTYPE CONSOLE} uses SysUtils; var Buffer: PChar; begin Buffer := StrNew('A fUnNy StRiNg'); try Writeln(Buffer); Writeln(StrLower(Buffer)); //Buffer has now been converted to all lower case. //This doesn't matter to StrUpper of course. Writeln(StrUpper(Buffer)); //However, we have lost the original casing, as //the next line shows. Writeln(Buffer); finally StrDispose(Buffer); end; Writeln; Write('Press ENTER to exit...'); ReadLn; end.
Or, perhaps even better, why not actually demonstrate the difference between StrXXX and their native string equivalents?
program Project1; {$APPTYPE CONSOLE} uses SysUtils; const Source = 'A fUnNy StRiNg'; var Buffer: PChar; S: string; begin Writeln('*** Native string version ***'); S := Source; Writeln(S); Writeln(LowerCase(S)); Writeln(UpperCase(S)); Writeln(S); Writeln; Writeln('*** PChar version ***'); Buffer := StrNew(Source); try Writeln(Buffer); Writeln(StrLower(Buffer)); //Buffer has now been converted to all lower case. //This doesn't matter to StrUpper of course. Writeln(StrUpper(Buffer)); //However, we have lost the original casing, as //the next line shows. Writeln(Buffer); finally StrDispose(Buffer); end; Writeln; Write('Press ENTER to exit...'); ReadLn; end.
Any thoughts?
Hello Chris,
I’ve added a link for this blog post to the Discussion page of the example in question:
http://docwiki.embarcadero.com/CodeSamples/en/SysUtilsStrLower_%28Delphi%29
Thank you very much for your constructive criticism and coding suggestions! I’ve put this on our work queue.
Regards, -Dee
Your last example is excellent.
– contrasting similar String and PChar functions in one example clarifies their differences and helps users making the distinction and warns them about the consequences of intermixing
– it’s good practice to use StrUpper/StrLower together with StrNew and StrDispose
– the comments tell what is happening
– creating a console app may be ‘ancient’, but for string examples I greatly prefer writeln above ‘Canvas.TextOut’. Changing labels or captions is simple, but not enough when the example needs more than one output line. (Memo.Lines.Add is not good for use in a code snippet.)
Victor — thanks for that. Just to add to what you say, the main problem I have with VCL-based examples for the StrXXX functions specifically is that you simply shouldn’t be using the latter in a purely VCL context, given the VCL presents itself with native Delphi strings. While any code example will inevitably be ‘unrealistic’ simply in being an ‘example’, this goes beyond that, since it may mislead newbies into thinking the StrXXX functions are what you should indeed be using in a VCL context, when you should in fact be using their native string equivalents.
Hi,
The content at http://docwiki.embarcadero.com/CodeSamples/en/SysUtilsStrLower_%28Delphi%29 has been updated, and work on the C++ version is in progress. Thank you again, and please feel free to make comments on the Discussion tabs of those pages. Regards, -Dee
Pingback: A fUnNy StRiNg « The Programming Works