Clone wiki

delphi-cvariants / Home

About

Yet another Delphi collections library. For reference see VType

Key features

  1. Automatic memory management
  2. Convenient object-style access
  3. Convenient open variant array arguments (array of const)
  4. Containers are not restricted to a single type
  5. Recursive operations such as Overlay, Diff & Patch. Snapshot is planned
  6. Import/export to YAML is in progress (see Delphi-YAML project)

What's wrong with another collections?

Benefits are easier to spot when reviewing what went wrong with another collections. I'm aware of SDL/DeCAL, ADSLD, dUnit collections, Delphi 2009 generics and several anothers.

Different collections use different roots.

The ones based on TObject

No automatic memory management. Annoying try .. finally. Things are getting worse when subcomponents can be shared amongst unrelated pieces of program. Object methods can't modify the variable they are being invoked from.

The ones based on IUnknown

No static methods and no initialization. Several operations must be implemented as functions either because they must be nil-resistant or because they need to be modify the variable.

The ones based on Variant

No static methods and no initialization, however, using a tiny hack dispatched calls can modify the variable. Variant dot-style invocations are always dispatched and so are error-prone to misspelling. Most real custom-variant examples use lot of functions instead of methods.

CVariants

So if everything is so bad, is there another way? Hell yes!

Delphi 2006 enhanced records so they can now contain methods, properties and private fields, but this is not the first time Delphi had this feature. Before Delphi had introduced by-ref objects deriving from a single root (TObject) there was another object system in Borland Pascal with Objects. And they are still supported by newer Delphi versions. Delphi 2006 records are more powerful (they can have overloaded operators), but there are absent in Delphi 7 & 2005. So we just use conditional directives to produce objects on Delphi 7 & 2005 and records on Delphi 2006+. They work right as expected and the only inconvenience is warnings about unsafe type. You can disable them with {$WARN UNSAFE_TYPE OFF} or in project options. Don't worry about freeing collections memory. There is a Variant inside and Delphi frees whatever is stored inside of Variant automatically. If you need to explicitly free the memory, you can use CVariant.Destroy method. It just assigns "Unassigned" to its internal Variant field. Delphi does the rest.

By wrapping Variant inside a record we've gained benefits of automatic memory management and at the same we have static methods and we can modify the variable (the inner Variant) inside of methods.

This is a tricky way of doing things, so it seems we are the first ones to use this approach.

Static methods make it easy to create and operate on maps and lists. Most scripting languages have something similar to Variant and maps and lists are directly available and can be assigned to Variant and be accessed inside. Delphi has Variant, but there is no convenient way of dealing with arrays. Visual Basic safe arrays and OLE Dictionary IDispatch are not convenient.

CVariants is what Delphi Variants were crying for from the very beginning.

What CVariants aren't

  • Other collections are developed in breadth. They give you an option to choose between different implementations of the same thing (LinkedList vs ArrayList). CVariants use "one size fits all" approach. This is because CVariant is developed in depth. When one is making a diff from two recursive trees (maps/lists) of data, it's not the best time to decide what implementation will we use for a particular sub-collection.
  • Also there is no any kind of item type specialization for the same reasons as above. There is no list of strings or a dictionary of integers (like in ADSDL or Delphi 2009 generic containers). Future work is possible, however, using RTTI or some kind of schema.
  • Despite CVariants hold Variant inside, maps and lists don't. Integer type details such as varSmallInt will be lost. Strings will become WideString on pre-Unicode Delphi and UnicodeString on Delphi 2009+. Custom Variants are not possible to put inside. These are temporary restrictions. This is because on the inner level CVariants use dUnit collections and convert values back and forth. It was easier to start from dUnit collection than writing everything from scratch. Future CVariants version might have another implementation.
  • Super-fast. The underlying IMap collection has two iterators: value iterator and key interator. But this is a bullshit, we need both simultaneously. So CMapIterator wraps key iterator and obtains value by key as opposed to getting it from iterator. This is a temporary limitation of current implementation. This is probably not even a major issue. A major issue is Clone which is needed for correct functioning. Clone can be replaced with Copy-On-Write semantics. Only time will show which direction to move.

Updated