Compile time ordered vectors

I would like to have support for ordering content of a constant array at compile time.

I would like to have optimized lookup function for such an ordered constant array (binary search, hash index, etc, – whatever fits the purpose) to check if a variable has a match in the ordered constant array. The code generator could inline the lookup function if so was desired.

I guess you can say that I am looking for something like the “in” keyword for sets, but extended to work with any constant array (string, array of enumerable, array of number, or array of other constants such as class type references).

A specification syntax could look something like:
   = {ordered {ascending | descending}} Vector

Implicit methods associated with the type:
  Vector.Contains(const element: T):Boolean;
  Vector.ContainsAny(const array of T):Boolean;
  Vector.ContainsAll(const array of T):Boolean;
  Vector.Include(const element: T);
  Vector.Exclude(const element: T);

Example declaration:
type
  TMyType = ordered ascending Vector;
const
  InvalidChars : TMyType = [‘\’, ‘:’, ‘.’, ‘(‘, ‘)’]; // I don’t want to worry about order here

Example of use:
  if InvalidChars.Contains(SomeCharacter) then …

Today, I can create singleton objects that are ordered as part of the initialization code, but it becomes a lot of micro management code which isn’t optimized for call performance, instead of easy to use code like “element in set” style code.

Forms and Data Entry Validation – Part 1

This is not an article about LiveBinding. I was once hoping it was going to be, but instead it has become an alternative to LiveBinding. If anything, it is about compile-time binding and quality assuring the data input from of your users.

Forms, forms, forms…

How many forms have you created?  Chance is – quite a few – and what do they have in common?   If people type rubbish, your data becomes rubbish.  So – what do you do?  You validate the input to prevent rubbish getting into the system.  You do… don’t you? Sure you do!

When do you validate it?  When someone clicks Submit or OK?  Right – then you have to go through the input, field by field, and first ensure that what the user typed in actually is understandable in the current context – such as no funny characters in an integer – and sometimes you have to check  the values against each other for logical states. If someone said they took three melons, their combined weight should at least be bigger than zero, and blue shirts don’t go well with pink pants, and what else not.

If the user typed in rubbish – you have to inform him or her so that it can be corrected.

Been there, done that

There is a certain amount of logic in this scene that we keep recreating scaffolding for.  Stuffing things into listboxes, formatting and filling in the values, validation of numbers and dates, converting enumerated types into strings (and back again). If you want the dialog to be slick – you might even want to validate as you go, which means eventhandlers for focus changes, keys pressed, UI items clicked, dropped down and selected, also adding to all the scaffolding code.

Some time ago, I had to create yet another dialog.  Lines and lines of housekeeping code that surround the real validation logic.  And naturally I don’t have to be clearvoyant to foresee numerous more such dialogs, as it is a major part of writing applications that deal with configuration, input and control.

So – I thought to myself – can I spend a little time now, and save a lot of time later?  Dangerous, innit, thinking like that…  suddenly you could find yourself writing a framework, and we all know what happens to frameworks, right?  They turn to endless amounts of code written with good intentions of handling the unexpected, covering functionality you won’t ever need, and at some point collapse on themselves to become a black hole of sketchily documented (since noone updated the docs as new features got added) , and hastily changed (since you always are in a hurry for that extra functionality) code.  And when someone else misread your framework intentions and applied it like a hammer to a screw – it just doesn’t end well.

Narrowing down the scope

Hence – Sticking with the KISS principle, I have decided to try to make it independent of other libraries, and limit what I implement to basic functionality while attempting to allow for future expansion.

I am going to create a TInput that wraps a GUI control.  To put it simply – a TInput that points to a specific TEdit, and takes care of stuffing values from the variable and into the GUI control, and vice versa.  The job of that TInput is the present the value correctly, and to ensure that what ever is written into that TEdit, can be converted into an integer.

I will also create a TInputList that is a collection of TInputs, that will have the job of going through the list to fill the controls, to validate the contents, and finally – if all input is syntactically correct – semantically validate the input for logical correctness.

Some of the code that I will present here, is probably centric to the type of data that I work on.  For me, an input form will  typically wrap an object with a set of properties that reflect a row or set of related rows in a database.  Why am I not using data aware controls?  Mostly because the applications we create actually can’t write to the database themselves, except through calling stored procedures that perform more magic before, during, or after the data has been written.  For that reason, the TInputList will be a TInputList, and the TInputList will have a property Current:T that I can populate, and each TInput will know that it is member of a TInputList, so that it can kick of the necessary actions for stuff to get validated.

[kom-pli-kei-tid]

By now you have probably thought to yourself: TEdit?  What about the other controls?

Because there are a number of input types, and a number of controls, and these make a number of combinations. TEdit/Double, TEdit/Integer, TEdit/String, and TEdit/Enum is already a list, and I haven’t even mentioned TComboBox yet,- so it is obvious that TInputList has to be polymorphic.

This brings us to the first part of complicated – creating a set of generic and polymorphic classes.  Generics in Delphi XE still don’t to well with forward declarations, and to create polymorphic parent/children lists, it really helps to be able to forward declare.

After some consideration, I have chosen to use an abstract class without generics as my inner base class.  TAbstractInput will know nothing about the data type we want to work with, nor will it know anything about the control type.  All TAbstractInput will do, is define the virtual abstract methods that will be our type agnostic operators or verbs and queries, if you like.  Hence, our TInputList will use TAbstractInput as its element type.

///  TAbstractInput defines the bare minimum base class for our list of inputs 

TAbstractInput = class abstract
private
protected
function GetEdited: Boolean; virtual; abstract;
procedure SetEdited(const Value: Boolean); virtual; abstract;
function GetEnabled: Boolean; virtual; abstract;
procedure SetEnabled(const Value: Boolean); virtual; abstract;
function ControlValueIsValid:Boolean; virtual; abstract;
function VariableValueIsValid:Boolean; virtual; abstract;
procedure FillControl; virtual; abstract;
procedure FillVariable; virtual; abstract;
procedure SetDisabledState; virtual; abstract;
procedure SetErrorState; virtual; abstract;
procedure SetNormalState; virtual; abstract;
procedure SaveNormalState; virtual; abstract;
procedure Setup; virtual; abstract;
public
procedure Clear; virtual; abstract;
procedure Update; virtual; abstract;
function Validate: Boolean; virtual; abstract;
property Edited: Boolean read GetEdited write SetEdited;
property Enabled: Boolean read GetEnabled write SetEnabled;
end;

From the outside of the list, we need TInput that expose the correct type that we want to access, so that will be our outer base class type – which knows how to set and get the value, and hence the class that we use to reference an input field.

///  TInput defines the input wrapper as we want it to be

/// visible from the outside of our list of controls

TInput = class abstract(TAbstractInput)
private
FOnCanGetValue: TGetValue;
procedure SetOnCanGetValue(const Value: TGetValue);
protected
function GetValue:T; virtual; abstract;
procedure SetValue(const Value:T); virtual; abstract;
function CanGetValue:Boolean; virtual; abstract;
public
property Value:T read GetValue write SetValue;
property OnCanGetValue: TGetValue read FOnCanGetValue write SetOnCanGetValue;
end;

Please note that this is a simplified view of TInput class.

Inside TInputList, I will subclass TInput again, and add knowledge of the controls.  In fact, I will create several subclasses that handle type conversions for each data type and control type, but instead of having the user instantiate all these different class types – I will add factory methods to the TInputList instead.

Here are some excerpts from the declaration of TInputList and the basic control wrapper.

///  TInputList is a wrapper for all our input controls. 

TInputList = class(TList)
...
public
type
/// This is our core input control wrapper on which we base wrappers for specific controls
TInputControl = class(TInput)
private
FController: TInputList;
FControl: TCtrl;
FValue: SVT;
...
end;
end;

Properties and Binding

This is the second part of complicated. Will I be using the XE2 LiveBinding? No. IMO, LiveBinding uses the least desirable method to bind a property for setting and getting. I lamented this in my previous article, Finding yourself in a property bind. In my opinion, LiveBinding is a good idea that is implemented in the wrong way, and in it’s current form will be vulnerable to property and variable name changes during refactoring. In addition, it appears that LiveBinding is not quite mature yet. Then there is the fact that XE and older, doesn’t have LiveBinding.

After some experimentation, I came to the conclusion that even if it appears to be more elegant to use visitors or observers and RTTI binding, I will get more flexibility, readability, and maintainability by using anonymous methods.

Anonymous methods allow me to do manipulation of the value before it is set/get, and allow the setter/getter events to have side effects. It also ensures that all references are validated compile-time. It will not guarantee protection from referencing the wrong properties and variables, but they will at least be of the right type, and actually exist.

Since my primary development platform is Windows, I am a VCL developer – and when I started this little project, I had only VCL in mind. However, as the code matured, I found that I might want to be able to use this for FireMonkey as well. That still remains to be seen as FireMonkey still smell of Baboon.

Still, the core logic is platform agnostic, and the VCL bits are separated into a unit of their own.

Here is an excerpt from the VCL implementation with complete declarations.

TInputListVCL = class(TInputList)

public
type
TInputControlVCL = class(TInputList.TInputControl)
protected
procedure ControlEnable(const aState:Boolean); override;
function ControlEnabled:Boolean; override;
procedure ControlSetFocus(const aFocused:Boolean); override;
end;

/// Basic wrapper for a TEdit
TEditTemplate = class abstract(TInputControlVCL)
private
FNormalColor: TColor;
protected
procedure SetControlValue(const Control:TEdit; const v:String); override;
function GetControlValue(const Control:TEdit): String; override;
function ControlValueAsString:String; override;
procedure SetErrorState; override;
procedure SetNormalState; override;
procedure SaveNormalState; override;
procedure SetDisabledState; override;
public
procedure Clear; override;
procedure Setup; override;
end;

/// TEdit wrapper for editing a string
TEditString = class(TEditTemplate)
protected
function ConvertControlToVariable(const cv: String; var v:String; var ErrMsg:String):Boolean; override;
function ConvertVariableToControl(const v:String; var cv:String):Boolean; override;
end;

/// TEdit wrapper for editing a float
TEditDouble = class(TEditTemplate)
private
FDecimals: Integer;
protected
procedure SetDecimals(const Value: Integer); override;
function GetDecimals:Integer; override;
function ConvertControlToVariable(const cv: String; var v:Double; var ErrMsg:String):Boolean; override;
function ConvertVariableToControl(const v:Double; var cv:String):Boolean; override;
end;

...

end;

Putting it to use

This will be covered in part 2. Until then, don’t forget to try out RAD Studio XE2 and join the RAD Studio World Tour presentations!

RAD Studio XE2 is RTM

The wait is over. If you have bought XE with Software Assurance, your SA Upgrade notice will be arriving shortly. If you want to try it out NOW, you can already download the 30-day trial editions.

Delphi XE2 Architect 30-day trial:
https://downloads.embarcadero.com/free/delphi

RAD Studio XE2 Architect 30-day trial:
https://downloads.embarcadero.com/free/rad_studio

Please note that the trial editions does not contain the command line compiler and VCL source code, as well as some third party offerings. You may want to install the trial editions in a VM, just as a precaution.

Until you got it installed, you might find pleasure in reading about the new release here: http://docs.embarcadero.com/products/rad_studio/, and perhaps in particular this section: What’s new in Delphi and C++Builder XE2.

This is a major release, with a lot of new features, so should you stumble upon a problem during your trial – please report any issues you find at http://qc.embarcadero.com

While you are at it, you should also sign up for the RAD Studio XE2 World Tour event near you.

In the Nordic countries:

Edit: Just had to add Andreano Lanusse’s XE2 ad image:

RAD Studio XE2 – VCL and FireMonkey (FMX) first glance comparison

Published with permission from Embarcadero.

When you start out a new simplistic GUI project in VCL or FireMonkey (FMX) in XE2, you will notice a few differences from older Delphi versions. XE2 introduce consistent use of unit scope prefixes. The Windows related units are grouped under the Winapi prefix, and the VCL units under VCL. FireMonkey has been given the short prefix FMX.

As you can see – the basic generated skeleton code is quite similar between the two frameworks.
Apart from referring different framework units, the resource include statement is also different: “*.dfm” vs “*.fmx”.

The skeleton code

unit MainUnitVCL;


interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
TFormVCL = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormVCL: TFormVCL;

implementation

{$R *.dfm}

end.

This is a FireMonkey HD project.

unit MainUnitFM;


interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs;

type
TFormFM = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormFM: TFormFM;

implementation

{$R *.fmx}

end.

The designer

The designers are fairly similar between the frameworks, but there are differences.
The good old VCL

The new FireMonkey designer:

If you are wondering what’s up with the drab grey look, you can relax. This is the designer look, not the run time look. FMX adopts to the theme of the OS it is running on – so it will look like a Windows app on Windows, and as a OSX app on OSX.

The form properties

Let’s take a quick look at the properties of the standard form.

We are clearly not in Kansas anymore. FMX is a completely different framework, and it has been designed with cross platform in mind, unlike VCL which was designed to wrap Windows controls. This means we can look forward to learning new tricks, which some of us will view as a plus, while others will sigh and mutter something about things being better before.

Porting a project from VCL to FMX is more work than recreating the form in the FMX designer, hooking up the new event handlers, and pasting in code from the VCL project.

I added some standard controls to both forms.

Design time

VCL

FMX

Run time

VCL

FMX

Source code

VCL

unit MainUnitVCL;


interface

uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
TFormVCL = class(TForm)
Button1: TButton;
Memo1: TMemo;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormVCL: TFormVCL;

implementation

{$R *.dfm}

procedure TFormVCL.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add(Edit1.Text);
Label1.Caption := IntToStr(Memo1.Lines.Count);
end;

end.

FMX

unit MainUnitFM;


interface

uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Edit, FMX.Layouts,
FMX.Memo;

type
TFormFM = class(TForm)
Button1: TButton;
Memo1: TMemo;
Label1: TLabel;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
FormFM: TFormFM;

implementation

{$R *.fmx}

procedure TFormFM.Button1Click(Sender: TObject);
begin
Memo1.Lines.Add(Edit1.Text);
Label1.Text := IntToStr(Memo1.Lines.Count);
end;

end.

As you can see – although similar in many ways – there are many differences between VCL and FMX. One example: TLabel.Caption is no more. TLabel.Text is the correct property under FMX.

Then there is the glaring fact that FMX HD will build for OSX as well as both 32 and 64-bit Windows 🙂 Alas – since I don’t have a Mac, I can’t show you the OSX version.

How do the form resources look?

VCL

object FormVCL: TFormVCL

Left = 0
Top = 0
Caption = 'FormVCL'
ClientHeight = 337
ClientWidth = 635
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 8
Top = 8
Width = 31
Height = 13
Caption = 'Label1'
end
object Button1: TButton
Left = 536
Top = 294
Width = 75
Height = 25
Caption = 'Button1'
Default = True
TabOrder = 0
OnClick = Button1Click
end
object Memo1: TMemo
Left = 8
Top = 24
Width = 498
Height = 265
Lines.Strings = (
'Memo1')
TabOrder = 1
end
object Edit1: TEdit
Left = 8
Top = 296
Width = 498
Height = 21
TabOrder = 2
Text = 'Edit1'
end
end

FMX

object FormFM: TFormFM

BiDiMode = bdLeftToRight
Caption = 'FormFM'
ClientHeight = 400
ClientWidth = 600
Left = 0
Top = 0
Transparency = False
Visible = False
StyleLookup = 'backgroundstyle'
object Button1: TButton
Position.Point = '(496,363)'
Width = 80.000000000000000000
Height = 22.000000000000000000
OnClick = Button1Click
TabOrder = 1
StaysPressed = False
IsPressed = False
Text = 'Button1'
Default = True
end
object Memo1: TMemo
Position.Point = '(8,24)'
Width = 473.000000000000000000
Height = 321.000000000000000000
TabOrder = 10
WordWrap = False
end
object Label1: TLabel
Position.Point = '(8,8)'
Width = 120.000000000000000000
Height = 15.000000000000000000
TabOrder = 11
Text = 'Label1'
end
object Edit1: TEdit
Position.Point = '(8,360)'
Width = 473.000000000000000000
Height = 22.000000000000000000
TabOrder = 12
ReadOnly = False
Password = False
end
end

FMX is made for scaling, and it also have numerous other nifty features such as rotation.

Summary

This was just a very shallow outline of some of the differences between VCL and FMX, but it is perhaps enough food for thought to make you realize that you most likely will have to put some effort into moving from VCL to FMX. In some cases, you can get away with minor adjustments, but when it comes to sophisticated GUI – or stuff that is based on Windows functions – you will need to rewrite parts of your code.

Don’t forget to visit http://www.embarcadero.com/world-tour to register for the RAD Studio XE2 World Tour event near you!

RAD Studio XE2 – Breaking out of 32-bit Windows

Published with permission from Embarcadero.

The Embarcadero RAD Studio XE2 World Tour is upon us.

The features are shiny! The reviews will be rave! As a Delphi developer, your favorite toolbox is about to get a massive upgrade that you don’t want to miss!

Click on the banner up top or down at the bottom of the blog to sign up for an event near you.

One of the new features is cross platform support.

On the long awaited end – your new Delphi will be able to do your old Windows 32-bit platform, and now also will compile your VCL applications to native 64-bit. Note that VCL will still be Windows Only. You may have to do a little fixing to your code, as Integer still is 32-bit but Pointer is 64-bit – so those old Pointer(Integer) casts will have to be reworked.

The most exiting feature of XE2 is that you can create GUIs for other platforms as well!
To enable this – Embarcadero nabbed the guy behind VGScene and DXScene and created FireMonkey – a whole new GUI framework that supports Windows, OSX and iOS in Delphi XE2, and that will (warning – speculation) very likely add support for other OS platforms in future versions of RAD Studio.

FireMonkey apps are cross platform – but they come in various flavors – so you still have to make a conscious choice about where to start…

Over the coming days I predict heavy blog activity with a lot to read about the new RAD Studio XE2 release, but in the mean time – make sure to sign up for the RAD Studio XE2 World Tour events!

Weird code snippet #2: Generic Double Linked List

They say Generics and pointers don’t mix.

type
PMyThing = ^TMyThing // [DCC Error] E2508 type parameters not allowed on this type
TMyThing = record
Thing: T;
end;

Ok, they don’t. But there is a loophole!

type
TMyThing = record
Thing: T;
NextThing: ^TMyThing;
end;

Why this is allowed, I don’t know. Just like I don’t really understand why the first one is forbidden. There is probably some good explanation for it.

Still – it can be fun breaking the rules!

unit GenericDoubleLinkedList;

interface
uses
Classes, Generics.Defaults;

type
TLinkVisitor = reference to procedure(const Item: T);

TDoubleLinked = record
Value: T;
PrevLink: ^TDoubleLinked; // Hey, it compiles!
NextLink: ^TDoubleLinked;
constructor Create(aValue:T);
function Add(aValue:T): TDoubleLinked;
function HasNext:Boolean;
function Next: TDoubleLinked;
function HasPrev:Boolean;
function Prev: TDoubleLinked;
function First: TDoubleLinked;
function Last: TDoubleLinked;
procedure ForEach(const Proc: TLinkVisitor);
end;

procedure Test(const Log:TStrings);

implementation

{ TDoubleLinked }

constructor TDoubleLinked.Create(aValue: T);
begin
Value := aValue;
NextLink := nil;
PrevLink := nil;
end;

function TDoubleLinked.Add(aValue: T): TDoubleLinked;
var
p: ^TDoubleLinked; // But this one is not assignment compatible
begin
p := AllocMem(SizeOf(TDoubleLinked)); // Make space
p^ := Self; // Copy current value to allocated block
Value := aValue; // Set self to new value
p.NextLink := @Self;
if Assigned(p.PrevLink) // Fix up previous nextlink
then Pointer(p.PrevLink.NextLink) := Pointer(p);
Pointer(PrevLink) := Pointer(p); // Point back to old value
Result := Self;
end;

function TDoubleLinked.HasPrev: Boolean;
begin
Result := PrevLink nil;
end;

function TDoubleLinked.Prev: TDoubleLinked;
begin
Result := TDoubleLinked(PrevLink^)
end;

function TDoubleLinked.HasNext: Boolean;
begin
Result := NextLink nil;
end;

function TDoubleLinked.Next: TDoubleLinked;
begin
Result := TDoubleLinked(NextLink^)
end;

function TDoubleLinked.First: TDoubleLinked;
begin
Result := Self;
while Result.HasPrev
do Result := Result.Prev;
end;

function TDoubleLinked.Last: TDoubleLinked;
begin
Result := Self;
while Result.HasNext
do Result := Result.Next;
end;

procedure TDoubleLinked.ForEach(const Proc: TLinkVisitor);
var
Node: TDoubleLinked;
begin
Node := First;
Proc(Node.Value);
while Node.HasNext
do begin
Node := Node.Next;
Proc(Node.Value);
end;
end;

procedure Test(const Log:TStrings);
var
List, Node : TDoubleLinked;
begin
List.Create('One');
List.Add('Two');
List.Add('Three');
Node := List; // Bad idea
List.Add('Four');
Node.Add('ThreeAndAHalf');

List.ForEach(
procedure(const Value:String)
begin
Log.Add('List: ' + Value)
end);

Node.ForEach(
procedure(const Value:String)
begin
Log.Add('Node: ' + Value)
end);
end;

end.

The problem is that “List” is not a pointer, but the tail item of the list. Hence, a Delete procedure needs to take this into consideration.

Even worse, if you add a second Node variable to point to something in the list, that reference will not be fixed up after adding ‘Four’, and hence it will take the tail place of the list – for both references, effectively forgetting the ‘Four’ item.

So, although this was somewhat entertaining, a mix of Generics and pointers probably isn’t something we should make use of.

Exercise for the reader: Implement the TDoubleLinked.Delete; procedure.

End question: Why are we not allowed to declare pointers to generic types?

Weird code snippet #1: Pseudobinary case statement

Curt Carpenter suggested a language addition for handing binary case logic.

That gave me this idea. Chalk it up as another weird code snippet from yours truly.

///> Convert array of booleans to a pseudobinary integer. 
///Good for up to 12 bits.

function BoolToInt(B: Array of Boolean):Integer;
var
x : Boolean;
begin
Result := 0;
for x in B
do begin
Result := Result * 10;
if x then Inc(Result);
end;
end;

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
ch : Char;
begin
ch := Lowercase(Char(Key));
case BoolToInt([ssShift in Shift, ssCtrl in Shift, ch='a', ch='x', ch='c', ch='v']) of
011000: SelectAll;
010100: Cut;
010010: Copy;
110010: Clone;
010001: Paste;
end;
end;

A slightly more tongue in cheek example:


procedure SexistTestLogic(const Cute, Funny, Smart: boolean);
begin
case BoolToInt([Cute, Funny, Smart]) of
000: Avoid;
001: Admire;
010,
011: Befriend;
100: Tolerate;
101: HandleWithCare;
110: Adore;
111: Marry;
end;
end;

Finding yourself in a property bind

From the wishful thinking department, I would love to be able to

var  
Src: TSomeClass;
MyObject : TBoundObject;
begin
MyObject.Bind(Src, CompilerMagic(TSomeClass.SomeBooleanProperty));
end;

instead of

var  
Src: TSomeClass;
MyObject : TBoundObject;
begin
MyObject.Bind(Src, 'SomeBooleanProperty');
end;

Why? Because the first one is resilient to property renaming.
Until I can, I am stuck with using something similar to

var
Src: TSomeClass;
MyObject : TBoundObject;
begin
MyObject.Bind(Src,
function (const Ref:TSomeClass):Boolean // reads the bound prop
begin
Result = Ref.SomeBooleanProperty;
end,
procedure (const Ref: TSomeClass; const Value:Boolean) // writes the bound prop
begin
Ref.SomeBooleanProperty := Value;
end);
end;

which actually has some benefits – such as being able to sanitize assigned values – but is way too verbose.

I wish…

Generics, Enumerated types and ordinal values

I wish this was a solution post, but it is a frustration post. I trying to figure out how I can use Ord() to convert an enumerated type to integer or cast an integer to an enumerated type using generics type arguments?

uses
SysUtils,
TypInfo;

type
TSomeEnumType = (TheFirst, TheSecond, TheThird, TheFourth);

type
TEnumGen = class
class function Name(const Enum:TEnumType):String;
class function Value(const Ordinal:Integer):TEnumType;
end;

class function TEnumGen.Name(const Enum: TEnumType): String;
begin
Result := Format('%s.%s',
[GetTypeName(TypeInfo(TEnumType)),
GetEnumName(TypeInfo(TEnumType), Ord(Enum)) // <-- Bombs
]);
end;

class function TEnumGen.Value(const Ordinal:Integer):TEnumType;
begin
Result := TEnumType(Ordinal); // <- Bombs
end;

Unfortunately there is no “enum” delimiter that can be used to tell the compiler that Ord() and casting of integers should be allowed for generic enumerated type arguments.