Posts tagged RTTI

DeHL documentation

Since I had a few requests, I have started putting together some documentation on DeHL. It’s pretty basic and only includes some conceptual stuff, not API documentation. You can find it here.

Version 0.5 is shaping up quite well with tons of changes, and new stuff coming in — but that’s a another story.

Not much in this post,  Cheers!

What’s new in DeHL 0.4

In the previous post I have mostly talked about Enex (Enumerable Extensions) I have enabled for all DeHL’s collection classes. Now I will list the other changes that got into this release that may be of interest to you:

  • Enex (Enumerable Extensions) in DeHL.Collections.Enex. Support for chained queries, predicates and selectors.
  • THashTree (A tree that uses hashes to access the child nodes). in DeHL.Collections.HashTree.
  • TConverter can be created with given IType‘s as opposed from the default ones.
  • TSuppressedWrapperType which can be used to wrap another IType. It’s main function is to forward all calls to the enclosed IType except the Cleanup() and Management() ones. This way you can use temporary collections inside your collection without the “cleanup” problem.
  • Fixed 2 bugs in Resize() method of THashSet and TDictionary which resulted in corrupted heap in some cases.
  • TLinkedList properties called First and Last were renamed to FirstNode and LastNode to avoid naming conflicts with Enex’s First() and Last() methods.
  • Fixed a bug in TQueue‘s enumerator object. It enumerated the queue’s elements quite wrong.
  • Renamed TScopedPtr to a better name: Scoped.
  • Added a new type: Nullable. It can be used to represent a NULL value for non-object types such as Integer.
  • New life-time extensions for TRefCountedObject. Each ref-counted object can now “keep-alive” other ref-counted objects. This was an useful addition in order to support query-chains in Enex.

How to use the new Nullable<> type:

function GetTheSum(const AList: TList<Integer>): 
    Nullable<Integer>;
begin
  { Check if the list is non-null and has elements }
  if (AList = nil) or (AList.Count = 0) then
    Result.MakeNull();
  else
    Result := AList.Sum();
end;

var
  LSum: Nullable<Integer>;
begin
  { ... }
  LSum := GetTheSum(...);
  if not LSum.IsNull then
    WriteLn(LSum.Value);
end;

I agree, this is not the best example, but it is OK to get you started. Nullable values are useful when you function may return or even accept a normal (let’s say Integer) value or a NULL.

DeHL 0.4: Linq-ifying Delphi

Since I have started working on DeHL, one of the main purposes was to get to the point where I could use Linq-like extensions on collection classes. Of course I could not call the new functionality Linq since it’s not language-integrated, so I decided to give it another name: Enex (aka Enumerable Extensions). All collection classes in DeHL now support a wide set of methods that allow querying and selecting elements from the starting collection. A lot of work has been put into this release, ranging from extensions to type system, object life-time management, optimizations and bug-fixes and of course a lot of testing.

Now, back to Enex, let’s visit some cool examples of what the new functionality can do:

Let’s check a very simple example. Assuming you have a list of integers and you want to find the sum of all elements that are greater than the list average. In the “times before” you wold write something like this:

  { Calculate the average of the list }
  Sum := 0;

  for I := 0 to List.Count - 1 do
    Sum := Sum + List[I];

  Avg := Sum div List.Count;

  { Calculate the sum of the elements above average }
  Sum := 0;

  for I := 0 to List.Count - 1 do
    if List[I] > Avg then
      Sum := Sum + List[I];

… Now it is as simple as this:

  { ... }
  Sum := List.WhereGreater(List.Average()).Sum();
  { ... }

Now, let’s see another more complex example. Considering that you have two collections of integers. You have to determine the maximum value common to both collections. Check it out:

  { ... }
  Max := AColl1.Intersect(AColl2).Max();
  { ... }

Next: what is the sum of all odd numbers in interval [0..100]?

  Sum := TEnexEnumerable<Integer>
    .Interval(0, 100)
    .Where(function(Arg1: Integer): Boolean
      begin
        Exit(Odd(Arg1);
      end)
    .Sum();

The last one: You want to take all integers from a list with their absolute value and only once; and you want to write them on the screen directly:

  for I in ARandomList
    .Select(function(Arg1: Integer): Integer
      begin
        Exit(Abs(Arg1));
      end)
    .Distinct()
  do
    WriteLn(I);

What’s working and what’s not:

  • Some problems in the compiler prevent the use of generic function Select<TOut> and Cast<TOut> for now. These two could have been used to transform each element of the collection to some other type.
  • Everything is based in interfaces to make use of automatical garbage collection. the only restriction is that the first enumerable (from which the chain starts) should be either an interface or must be destroyed explicitly.
  • All collections have overriden versions of some functions. For example in a list, the First() method is implemeted directly so that you benefit from full speed whenever possible.
  • No lambdas … that makes writing selectors or predicates rather unpleasant.
  • Sum() and Average() are only supported for types registered as integers or reals (including BigCardinal and BigInteger).
  • All selector and predicate-based selection is done on-the fly so there is no additional memory used!

There are of course much more possible use cases for Enex-enabled collections, I simply do not have the time to list all of them.

You can download the latest version of DeHL here.

DeHL 0.3 is out

I’m proud to announce the 0.3 version of DeHL library. This release focused on bug-fixes and extensions to type support engine. This version should pretty much stabilize what I started and should leave me more time to extend other interesting parts of the library such as collections.

For those who are new, DeHL is a project of mine designed to fill in some holes in the Delphi’s RTL — most notably, some centralized and OOP-ish Date/Time support, more generic collection classes and “big integer” math support. DeHL also builds up a new layer of “type support” which is very useful to abstract all type management tasks in generic classes. This projects only supports Delphi 2009 (and above) since Generics, Anonymous methods, and Unicode are widely used whithin the library. So even if you are not interested in the library itself, it may prove a good read if you are interested in all new features Delphi 2009 has to offer.

New features:

  • Type support classes now expose methods to convert a type from an to a Variant.
  • Based on the newly added Variant conversion, a new type: TConverter<T1, T2> is present. You can use it to support “blind” conversion in a class.
  • Much improved type support system
    • Now more functionality is split between generic and non-generic variants of the type support classes (IType, IType<T>, TType, TType<T>).
    • TypeSupport has been renamed to TType and IType repectively.
    • IType/TType now export Name, Size, Management, TypeInfo and Family properties. You can use those properties to get more information about your generic type.
    • Custom type registration has been improved. A new cleaner API can be used to register you custom types into the DeHL’s type system.
    • TType<T>.Default is now an overloaded function. The first form is the usual one. The second form receives a set of “type families” that are expected to represent the generic type. This way you can effectively impose run-time type restrictions.
    • TClassType is now generic with T being class-restricted. This allows to avoid compile-time type incompatibilities.
  • All important standard types in Delphi are now supported. This support required the working custom types system, since these types can’t be handled “by default“:
    • Boolean, ByteBool, WordBool, LongBool
    • TDateTime, TTime, TDate
    • NativeInt, NativeUInt
    • Real
    • OleVariant
    • UCS4Char, UCS4String
    • UTF8String
    • RawByteString

Bug fixes:

  • Fixed a bug in BigCardinal and BigInteger variant types related to string conversion.
  • A few changes in TDictionary to avoid compiler problems.
  • In a multi-threaded environment, the custom type support would not unlock properly leading to a dead-lock.
  • Cleanup routines for TDictionary and THashSet were flawed since those used FillChar to clear the array. Managed types had problems with that.
  • Char type support class now acts properly.

Note: It may be possible that the Testing project will not compile due to a bug in the Delphi compiler. But that should not prevent the usage of the library itself.

The download section can be found here.

DeHL 2.2 Released

In this release I have changed the name of the library from DelphiHelperLib to DeHL to be shorter. The most important changes in this release are:

  • Renamed the library to DeHL in hopes to remove the length of the unit names.
  • Renamed all classes to use the T prefix rather than H. It was an unfamiliar mode of naming things.
  • BigCardinal and BigInteger can be used within a Variant type. An implicit operator is declared to allow seamlessly to convert from the BigInteger/Cardinal to Variants.
  • Type Support has been extended to offer better information about the clean-up practices of a type.
  • Added methods to move a number of elements from an array safely (for ref-counted Delphi types). Also fixed the code to use these methods instead of Move directly.
  • Added a new array type: TFixedArray. It is an immutable array which is used only for reading. TDynamicArray can be created off TFixedArrays.
  • Added work-around fixes for a few Delphi bugs blocking the compilation.
  • The testing module was cleaned up a bit. Now using a base class for all our test cases. Also tests for new scenarios have been added.
  • All container classes now cleanup their contained values (when required). This means it is now safe to use them with types that require cleanup.
  • Cleaned up the binary tree implementation a bit and fixed a bug.
  • Externalized the last remaining hard-coded strings in the code.

More information and downloads can be found here.

HelperLib 0.2 released!

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.

Forcing RTTI on record types

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.