The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,261 other subscribers

From Delphi 1: Type Compatibility and Identity

Posted by jpluimers on 2020/09/30

A feature overlooked by many Delphi programmer was already introduced in Delphi 1 which is more or less the same as in the Delphi 2007 documentation at [WayBack] Type Compatibility and Identity.

There is a distinction between these explained in the above link:

type
  TMyInteger1 = Integer;
  TMyInteger2 = type Integer;

Where TMyInteger1 is an alias for Integer, TMyInteger2 introduces a new type which is distinct from Integer and TMyInteger. That way the compiler can set them apart, and even generates separate RTTI (Run-Time TypeInformation) for them.

Probably the most used distinct types are these:

TDateTime = type Double;
...
TDate = type TDateTime;
TTime = type TDateTime;
TFontName = type string

These are unlike TColor which is defined as “just” a subrange of Integer, but because it is a subtype, also gets a distinct type:

TColor = -$7FFFFFFF-1..$7FFFFFFF;

Type identity is important because Delphi 1 introduced these mechanisms:

  • the streaming instances and their properties
  • editing instances and properties in the object inspector
  • two way binding of designer (form/datamodule/frame/…) and the underlying Pascal source

Without them, very basic Delphi features would not work.

In addition, a lot of other RTTI based code now enables features like object relational mapping, binding to JSON/XML and many others.

What I did not know is that the Pascal and Delphi type systems have been heavily influenced by ADA. Luckily Lutz Donnerhacke pointed me to ADA [WayBack] Types and Subtypes.

Example

I made an example Distinct type types in Delphi · GitHub showing the differences on RTTI level in these properties:

property IntegerProperty: Integer read FIntegerField write FIntegerField;
property ColorProperty: TColor read FColorField write FColorField;
property DoubleProperty: Double read FDoubleField write FDoubleField;
property DateTimeProperty: TDateTime read FDateTimeField write FDateTimeField;
property DateProperty: TDate read FDateField write FDateField;
property TimeProperty: TTime read FTimeField write FTimeField;
property StringProperty: string read FStringField write FStringField;
property FontNameProperty: TFontName read FFontNameField write FFontNameField;

The generated table (see also the source below using [Archive.is] TRttiContext added in Delphi 2010) indeed shows distinct types on the RTTI level:

Name Type.Name Type.QualifiedName Type.TypeKind
IntegerProperty Integer System.Integer tkInteger
ColorProperty TColor System.UITypes.TColor tkInteger
DoubleProperty Double System.Double tkFloat
DateTimeProperty TDateTime System.TDateTime tkFloat
DateProperty TDate System.TDate tkFloat
TimeProperty TTime System.TTime tkFloat
StringProperty string System.string tkUString
FontNameProperty TFontName System.UITypes.TFontName tkUString

This post was inspired by an interesting discussion on [WayBack] What’s the technical term for the following construct: type intx = type integer; type inty = integer; What term would you use to describe the differen… – Johan Bontes – Google+

Documentation:

RTTI dump inspired by [WayBack] delphi – How can I distinguish TDateTime properties from Double properties with RTTI? – Stack Overflow.

–jeroen


program RttiForDistinctTypedTypesConsoleProject;
{$APPTYPE CONSOLE}
{$R *.res}
uses
// System,
System.Classes,
System.SysUtils,
System.Rtti,
System.UITypes;
// Dump based on https://stackoverflow.com/questions/7836880/how-can-i-distinguish-tdatetime-properties-from-double-properties-with-rtti
// The easiest to dump RTTI is by using a construct that:
// 1. gives you a TypeInfo type information handle, like a class having a ClassInfo function.
// 2. allows easy enumeration (again a class)
type
TInstance = class
strict private
FIntegerField: Integer;
FColorField: TColor;
FDoubleField: Double;
FDateTimeField: TDateTime;
FDateField: TDate;
FTimeField: TTime;
FStringField: string;
FFontNameField: TFontName;
public
property IntegerProperty: Integer read FIntegerField write FIntegerField;
property ColorProperty: TColor read FColorField write FColorField;
property DoubleProperty: Double read FDoubleField write FDoubleField;
property DateTimeProperty: TDateTime read FDateTimeField write FDateTimeField;
property DateProperty: TDate read FDateField write FDateField;
property TimeProperty: TTime read FTimeField write FTimeField;
property StringProperty: string read FStringField write FStringField;
property FontNameProperty: TFontName read FFontNameField write FFontNameField;
end;
type
TMarkdownSeparator = (Line, Row);
procedure MarkdownDump(
const aMarkdownSeparator: TMarkdownSeparator;
const aName: string;
const aType: string;
const aQualifiedType: string;
const aKind: string);
var
FixString: string;
SeparatorString: string;
begin
case aMarkdownSeparator of
Line:
begin
FixString := '-';
SeparatorString := '-|-';
end;
Row:
begin
FixString := '`';
SeparatorString := '` | `';
end;
end;
Writeln(Format('%s%s%s%s%s%s%s%s%s', [
FixString,
aName, SeparatorString,
aType, SeparatorString,
aQualifiedType, SeparatorString,
aKind,
FixString
]));
end;
procedure Main;
var
RttiContext: TRttiContext;
InstanceRttiType: TRttiType;
RttiProperty : TRttiProperty;
PropertyType: TRttiType;
begin
RttiContext := TRttiContext.Create();
MarkdownDump(
Row,
'Name',
'Type.Name',
'Type.QualifiedName',
'Type.TypeKind'
);
MarkdownDump(Line, '-', '-', '-', '-');
try
InstanceRttiType := RttiContext.GetType(TInstance.ClassInfo);
for RttiProperty in InstanceRttiType.GetProperties() do
begin
PropertyType := RttiProperty.PropertyType;
MarkdownDump(
Row,
RttiProperty.Name,
PropertyType.Name,
PropertyType.QualifiedName,
TRttiEnumerationType.GetName<TTypeKind>(PropertyType.TypeKind)
);
end;
finally
RttiContext.Free();
end;
end;
begin
try
Main();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.

Name Type.Name Type.QualifiedName Type.TypeKind
IntegerProperty Integer System.Integer tkInteger
ColorProperty TColor System.UITypes.TColor tkInteger
DoubleProperty Double System.Double tkFloat
DateTimeProperty TDateTime System.TDateTime tkFloat
DateProperty TDate System.TDate tkFloat
TimeProperty TTime System.TTime tkFloat
StringProperty string System.string tkUString
FontNameProperty TFontName System.UITypes.TFontName tkUString

view raw

table.md

hosted with ❤ by GitHub

2 Responses to “From Delphi 1: Type Compatibility and Identity”

  1. abouchez said

    I use this pascal type definition, with strong typing abilities (limited in compiler terms but more explicit anyway), a lot when defining some DDD objects.
    It avoid problems like https://en.wikipedia.org/wiki/Mars_Climate_Orbiter when you don’t know which units a value holds.

    But it is to note that once you define a sub-type, e.g. as “TAgeInYears = type integer”, I discovered some years ago that you loose the abilities to use helper methods like age.ToString.
    This was IMHO a flow in the helper methods implementation.
    Does this problem remain?

    • HeartWare said

      On the other hand, “TAgeInYears = type integer” allows you to introduce your own helper functions for TAgeInYears without impacting the ones for integer (which I use on some occasions).

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.