Just released the “fix” version for HelperLib: 0.2.1.
Major highlights:
- Improved the performance for IntToStr functions for “big numbers”. (From 25 minutes to 17 seconds for a 256^10000 number). A BCD-izer algorithm was used to create a BCD version of the BigCardinal and the apply a simple “4-bit digit” to “char” translation.
- The same was done for IntToHex.
- Small numbers use the RTL provided IntToStr and IntToHex routines.
- Added the last missing piece of the Collections pack: HMultiMap.
- … and a number of other changes.
Link to the project page: Delphi HelperLib.
I have finally released the version 0.2 of the HelperLib library. If you’ are interested in using generics in your Delphi 2009 applications, you might actually be interested in taking a look at the library.
What can you find in HelperLib:
- Date and time manipulation. All packed nicely in records with overloaded operators and utility functions.
- Big numbers. The library includes support for BigInteger and BigCardinal data-types. Both types have overloaded operators, utility functions and etc goodness.
- “Type Support”. My equivalent to Generics.Defaults present in Delphi. I’ve chose to go my way because of the style the Generics.Defaults is written in and mostly because my version is extensible and supports more options.
- Collection classes like List, Dictionary, HashSet, Bag, LinkedList and more.
- All code is unit-tested to avoid most bugs.
The big difference between the Delphi’s implementation of generic collections and what is provided by HelperLib is that the control over the lifetime of the data stored in the collection is given to the “Type Support” classes rather than the collection itself. This primarily means that there is no object version of each collection class and there are no options like “OwnsObjects“. By default the “Type Support” that manages classes assumes that those must be freed upon collection destruction or clearing. Of course you can override that.
Anyway, take a look here for a full feature list. The download link is here.
Note: The code is hosted on Google Code and is BSD licensed.
While developing the “Type Support” module for the generics library I’m working on I’ve encountered a little problem – the compiler will not emit RTTI information for the records. Actually that isn’t entirely true – RTTI will be emitted if a record contains a “managed type” like dynamic arrays, strings, interfaces or reference to functions. The compiler needs that RTTI on records because it needs to initialize and finalize those managed types.
Now, back to our problem. Why would you need RTTI on a record in the first place? Well, in my case the I’ve added a possibility to register custom “type support” classes for any of your custom data types (which happen to be records). To be able to retrieve those classes later the TypeInfo is required to be unique for those data types which also means it must exist!
After some fiddling around, I’ve come up with a hacky way to force the compiler to emit RTTI on my records. Check out the code below:
uses
SysUtils, TypInfo;
type
TMyRecord1 = record
strict private
FForceRTTI: IInterface;
public
{ ... Any fields you want here ...}
end;
begin
WriteLn(PTypeInfo(TypeInfo(TTypeInfo))^.Name);
Readln;
end.
Notice the strict private FForceRTTI field that is of type IInterface. It ensures that the compiler knows that we have a “managed type” in our record so it will emit RTTI information for it! The disadvantage of this little hack is that the compiler will be forced to add initialization and finalization code each time you are using this record in your code. It will do that to ensure that the FForceRTTI field is always reference counted and deallocated when requuired. Until the compiler team adds support for records in RTTI this hack will do.
P.S. If you’re curious what the heck is that “type support” I was mentioning take a look here.
This post describes how to avoid a recent mistake I’ve seen in a Delphi application related to “Pointer to String” casting. Consider the following example:
var
PSomeStrData: Pointer;
SomeStr: String;
begin
{ Allocate 128 bytes of memory }
PSomeStrData := GetMemory(128);
{ Fills in the memory pointed with a C-style string }
FillWithACStyleStr(PSomeStrData, 128);
SomeStr := String(PSomeStrData);
WriteLn(SomeStr);
end;
In this example an untyped pointer was allocated with 128 bytes of memory, then filled with some C-style string (FillWithACStyleStr doesn’t exist, I just used it as an example here). After that, a pointer-to-string cast was performed, and finally the resulted string was printed to the console. This code is wrong! The assignment: “SomeStr := String(PSomeStrData);” will be converted by the compiler to a _UStrAsg (in System.pas) which receives 2 parameters (SomeStr and PSomeStrData) of type UnicodeString. This means that PSomeStrData is treated as an UnicodeString rather than the expected char*! This will probably result in some access violation because the memory _before_ PSomeStrData must contain the standard StrRec structure each string has.
The most simple solution to this hypothetical bug is to use PChar instead of Pointer for PSomeStrDatavariable. This ensures that the compiler will emit the correct call to _UStrFromPWChar which copies the data over to SomeStr.
Was just reading the source code for ShineOn (the RTL implementation in Delphi Prism) and found an interesting piece: DelphiString (in /ShineOn.RTL/DelphiString.pas).
What is it? It is a class that tries to replicate the behavior of the Win32 Delphi’s String type.The difference between Delphi’s String and .NET string is that the first one is mutable (aka can be written to but not thread-safe) and the second is immutable (can’t be modified making it thread-safe but harder to use).
If you are porting an application over Win32 Delphi you can’t normally use .NET’s String class because most of your code surely makes operations on strings.
Back to DelphiString; you might be tempted to use it simply by replacing all instances of String with DelphiString in your code — WRONG! Because Win32 Delphi uses a technique called Copy-On-Write, code like this:
S1 := 'John'
S2 := S1;
S2[1] := '_';
ASSERT((S1= 'John') and (S2 = '_ohn'));
… will work as expected (in Win32 Delphi) even if S2 is initially pointing to the same string as S1! In Delphi Prism, S2 will point to the same object as S2, but no “copy-on-write” will be performed and both strings will eventually be changed to “_ohn”. A simple “replace all String with DelphiString” will not be enough. I would kindly suggest that the application gets ported to use .NET String class rather than DelphiString.