It often happens in programming that you need to name two things that are
similar but different. This can be a difficult task, but one would expect
that if you were designing a language or its standard library you’d put
extra effort into your naming. Sadly, it sometimes seems that the people
designing Delphi didn’t put in that effort. For a simple example,
consider division by zero. If you try to divide by zero, Delphi generates
an exception at runtime, but if you were doing an integer divide the
exception is EDivByZero
, whereas floating-point division generates
EZeroDivide
.
For a more extreme example, let’s look at String comparisons. Delphi has functions for native Delphi strings and C-style null-terminated strings, functions for case-sensitive and case-insensitive comparison, functions that understand multibyte character coding systems and use Windows locale info and functions that don’t, and some null-terminated functions that also take a character count (analogous to strncmp).
Function | String Type | Multibyte-aware | Case-Sensitive | Specify Length |
---|---|---|---|---|
= |
String |
No | Yes | No |
CompareText |
String |
No | No | No |
CompareStr |
String |
No | Yes | No |
AnsiCompareText |
String |
Yes | No | No |
AnsiCompareStr |
String |
Yes | Yes | No |
StrIComp |
PChar |
No | No | No |
StrLIComp |
PChar |
No | No | Yes |
StrComp |
PChar |
No | Yes | No |
StrLComp |
PChar |
No | Yes | Yes |
AnsiStrIComp |
PChar |
Yes | No | No |
AnsiStrLIComp |
PChar |
Yes | No | Yes |
AnsiStrComp |
PChar |
Yes | Yes | No |
AnsiStrLComp |
PChar |
Yes | Yes | Yes |
Granted, the null-terminated functions are pretty consistent with their Ls
and Is, but I can never remember which of CompareStr
and CompareText
is which (not to mention remembering which names are for null-terminated
strings and which are for Delphi strings). And the rule of thumb is to
always use the Ansi
functions, because they honor locale orderings.
Presumably the non-Ansi
functions are around because they’re faster
(they do a straight numeric compare on the ordinal value of each
character), but the naming implies that they should be the default.
Another hairy mess of naming I run into moderately often is the task of
turning a TDateTime
into a string. Should I use DateToStr
or
TimeToStr
, which each take one parameter (the TDateTime
variable) and
use several global variables to determine their formatting? Should I use
FormatDateTime
, which takes two parameters: the TDateTime
variable and
a format string? Should I use DateTimeToStr
, which works just like
FormatDateTime
, but takes a third parameter, a string variable passed by
reference, into which the result is placed? (DateToStr
, TimeToStr
,
and FormatDateTime
are Delphi functions while DateTimeToStr
is a
Delphi procedure.)
I consider Format
to be Delphi’s analog to sprintf
, and use it
exclusively, but Delphi provides FmtStr
, Format
, and FormatBuf
.
Format
is a function that returns a formatted Delphi string. FmtStr
is a procedure that gets its result variable passed in by reference.
FormatBuf
is the direct analog to snprintf
: you must pass in the
result variable and the format string as PChar
s, as well as the lengths
of those two strings; the result is placed into the passed variable and
the function returns the length of that string.
StrUpper
upcases an entire PChar
. AnsiStrUpper
does the same, but
understands multibyte encodings and honors the Windows locale.
UpperCase
and AnsiUpperCase
are the same, but for Delphi strings.
UpCase
upcases a single character.
Low
gives the lowest value of a range type or the lowest index of an
array. Lo
gives the low-order byte of an integer. Likewise, High
gives the highest value of a range type or the highest index of an array
while Hi
gives the high-order byte of an integer. (More properly, Hi
returns the second-lowest-order byte of an integer; it assumes all
integers are 16-bit.)
Trunc
takes a floating point number and truncates it to an integer.
Truncate
deletes all data in a file past the current seek point. I use
Trunc
just rarely enough that I almost always use Truncate
when I mean
to use Trunc
.
I suppose all languages have similar issues (I’m reminded of Common Lisp
with aref
, svref
, bit
, sbit
, nth
, elt
, char
, schar
, and (I
suppose) row-major-aref
), but in most other cases, I don’t often find
myself trying to decide which of several similarly-named functions is the
right one and which is wrong. Delphi’s setup just feels more muddied to
me.