December 30, 2012

Writing an iterator for a container


In my last article I explained how to write a class property being a list of integers and how to write the corresponding property editor. We wrote TIntegerList class which merely looks like the well known TStringList class except it contains integers instead of strings.

Today, we will add some code to TIntegerList class so that we can use the for..in Delphi construct. Without this construct, the code to iterate thru the list of integers and display those in a TMemo looks like this:

procedure TForm1.EnumeratorButtonClick(Sender: TObject);
var
  I, N : Integer;
begin
  for N := 0 to TickLine1.TickPos.Count - 1 do begin
    I := TickLine1.TickPos[N];
    Memo1.Lines.Add(IntToStr(I));
  end;
end;

Using this for..in construct greatly simplifies the code:

procedure TForm1.EnumeratorButtonClick(Sender: TObject);
var
    I : Integer;
begin
    for I in TickLine1.TickPos do
        Memo1.Lines.Add(IntToStr(I));
end;


For the compiler to be able to iterate thru our TIntegerList class (A "container" in Delphi terminology), we must implement a new public method GetEnumerator() function which has to return a class, a record or an interface. In my example, I will use a class.

The class must contain a function MoveNext() returning a boolean and a property Current returning the current element of the container.

We will name the enumerator class TIntegerListEnumerator. To help you better understand here is the code the compiler actually generates when you write the code above:

procedure TForm1.EnumeratorButtonClick(Sender: TObject);
var
  I    : Integer;

  Enum : TIntegerListEnumerator;
begin

  Enum := TickLine1.GetEnumerator();
  try
      while Enum.MoveNext do begin
          I := Enum.Current;
          Memo1.Lines.Add(IntToStr(I));
      end;
  finally
      Enum.Free;
  end;
end;


Remember, you don't write this code, it is generated by the compiler when you write the "for I in".
The code is quite easy to understand. The actual enumeration is done by the class TIntegerListEnumerator we have to write once. And it is trivial to write it. Here is the source code:

    TIntegerListEnumerator = class
    private
        FIndex    : Integer;
        FIntegers : TIntegerList;
    public
        constructor Create(const AIntegers: TIntegerList);
        function GetCurrent: Integer; inline;
        function MoveNext: Boolean;
        property Current: Integer read GetCurrent;
    end;


constructor TIntegerListEnumerator.Create(const AIntegers: TIntegerList);
begin
    inherited Create;
    FIndex    := -1;
    FIntegers := AIntegers;
end;


function TIntegerListEnumerator.GetCurrent: Integer;
begin
    Result := FIntegers[FIndex];
end;


function TIntegerListEnumerator.MoveNext: Boolean;
begin
    Result := FIndex < (FIntegers.Count - 1);
    if Result then
        Inc(FIndex);
end;


The class has two variables: FIndex and FIntegers. FIndex will be used to iterate thru all values and FIntegers will reference the values to be iterated.

The construction takes one argument which is the integer list to iterate. It initializes the FIndex variable to -1 which is the starting value since it gets incremented before each iteration by MoveNext.

The procedure MoveNext has two responsibilities: increment the iteration variable and return the status of the iteration. If MoveNext returns TRUE, then it means there are more items in the list.

The property Current, implemented using the setter GetCurrent simply returns the integer whose index is given by the iteration variable FIndex.

That all we need for the iteration class!

The actual container class, TIntegerList need only one public method: GetEnumerator. The code is really simple:

function TIntegerList.GetEnumerator: TIntegerListEnumerator;
begin
    Result := TIntegerListEnumerator.Create(Self);
end;


That's it !

Note: Have a look at Internet Component Suite (ICS)

December 24, 2012

TIntegerList property and property editor






A few weeks ago, I was faced with the problem of designing a component having a list of integer as a property. This is quite easy to write, but once installed in the IDE, this property cannot be edited using the object inspector! Actually, the property is not even serialized in the DFM.

After a few Google searches, I came to the conclusion that no code was readily available for the purpose. So I wrote it and now I'm making it available thru this blog article.

This article is made of:

1      A simple custom visual component using a list of integers as property. This component has been made very simple (and mostly useless) so that you can concentrate of our today's subject: the property made of a list of integers.

2      A simple demo application showing the component in action at runtime and reusing the property editor in the application (normally a property editor is only used by the IDE).

3      A class to implement the list of integers. It looks much like TStringList class. Instead of a list of strings, we get a list of integers.

4      A property editor so that the list of integer could be edited from the object inspector at design time.

Read more: TIntegerList property and property editor
Have a look at Internet Component Suite (ICS)

December 11, 2012

Delphi XE3 and C++Builder XE3 Update 1 ISO available

The ISO track for Delphi XE3 and C++Builder XE3 is available now for registered users of the product. It include update 1 built-in. Download form here

This ISO can be used to burn a DVD or can be directly mounted with such tool as Daemon Tools Lite.


November 18, 2012

XE3: Adding properties to a set

[Français]
Delphi XE3 has a very interesting new feature: the record helpers.

Despite his name which reference a specific data type (record), a record helper can be applyed to most standard data types as well, include the sets.

Using a record helper, we will be able to add properties and methods to sets.

In this artcile, I will show you how it works using a simple case: adding a property to a set allowing the developer to convert the set to an integer or to assign a set with an integer.

You'll ask: Is it really useful in the real world ?

Yes it is! Here is the story which led me to this feature. I was working on a driver for a input/output card (I/O card). This card was driving an industrial machine. Most I/O card have "registers" which are used to program the hardware feature. Each bit in a register has a specific function such as turning on a relay or starting a moter, or reading a limit switch. Such a register can easily been represented by a Delphi set. Each bit correspond to a value in the enumeration which is the base of the set. When you read or write a register in the I/O card, you don't read or write a set but an integer. You have to add some code to transfrom the integer to/from the set so that inside the whole Delphi program, things are readable and easy to handle.

So this is the story. Of course I won't make things complex here and will translate this real world situation to something easier to grasp for anyone not accustomed to industrial control. I will replace the register by a basket with some fruits.

The basket is somewhat special: it can contain zero or more fruits, but at most one fruit of each kind taken from a fruit list.

Translated to Delphi, this gives:
type
 TFruit = (frApple, frPear, frApricot, frCherry);
 TFruits = set of TFruit;
Having those declarations, we can create variables and handle the set of fruits:
var
  Basket : TFruits;
begin
  Basket := [frCherry, frPear];
  Basket := Panier + [frApple];
  if frPear in Basket then
     Memo1.Lines.Add('Basket contain a pear');
end;
This is actually standard Pascal as it has always existed. You'll find a lot of article on the net which will describe the set usage using Pascal.
But Delphi XE3 has much more features than standard Pascal! The one wi'll speak in a moment is the possibility to add methods and properties to a set. I will show you now how to apply this feature to convert our fruit basket into an integer and the reverse.

Let's be clear: there are a lot of ways doing this kind of conversion. I will use thoe one which is the most respectful of the language because it doesn't make any assumption about how the compiler internal represents a set.

The goal is to be able to write this kind of code:

var
   Basket : TFruits;
   N : Integer;
begin
   Basket := [frCherry, frPear];
   // Convert to an integer
   N := Basket.Integer;
   // Convert from an integer
  Basket.Integer := 5;
end;

Look at the notation: "Basket.Integer". It is just like the Basket variable is a class and this class has a property named Integer. But Basket is not a class, it is a set.

Here is how to do that:

type  
  TFruitsHelper = record helper for TFruits
  strict private
    function  ToInteger : Integer;
    procedure FromInteger(Value : Integer);
  public
    property Integer : Integer read  ToInteger
                               write FromInteger;
  end;

implementation

function TFruitsHelper.ToInteger : Integer;
var
   F : TFruit;
begin
   Result := 0;
   for F := Low(TFruit) to High(TFruit) do begin
     if F in Self then
       Result := Result or (1 shl Ord(F));
   end;
end;

procedure TFruitsHelper.FromInteger(Value: Integer);
var
  F : TFruit;
begin
  Self := [];
  for F := Low(TFruit) to High(TFruit) do begin
    if (Value and (1 shl Ord(F))) <> 0 then
      Self := Self + [F];
  end;
end;


That's it! The code assign a single bit into the integer to each of the enumerated type element. To do that, I have used:

  1. A loop for F := Low(TFruit) to High(TFruit) do used to scan the enumeration items without ever specfying any identifier;
  2. The intrinsic function Ord() which returns the index of the element in the enumerated type (frApple=0, frPear=1 and so on);
  3. The operator shl which shift a number to the left. The shithed number is 1 here. The result is a bit set to 1 at the position in the integer given by the index.
  4. The operator in which tells if a given enumeration element is or isn't present in the set.
  5. The operator and which I use to mask the interger bits to know if one specific bit is 1 or not.
Done...

Notes:
  1. This code assume the set is small enough to be contained in an integer (32 bit). You can easily change it to support 64 bit integer. My initial target was to represent an I/O card register and they almost always fit in an integer.
  2. Internally Delphi compiler is using a single bit to represent an enumeration item inside the set. Knowing that, it is possible to change the code to take advantage of it. It is faster but dependent on the internal implementation and could be broken in a future release. The code could look like this:
  function TFruitsHelper.ToInteger: Integer;
  begin
    {$IF SizeOf(TFruits) = SizeOf(Byte)}
      Result := PByte(@Self)^;
    {$ELSEIF SizeOf(TFruits) = SizeOf(Word)}
      Result := PWord(@Self)^;
    {$ELSEIF SizeOf(TFruits) = SizeOf(Integer)}
      Result := PInteger(@Self)^;
    {$ELSE}
      {$MESSAGE FATAL 'TFruits cannot be represented as an integer'}
    {$IFEND}
  end;
--
François Piette
Embarcadero MVP
http://www.overbyte.be



November 17, 2012

XE3: Ajouter des propriétés aux ensembles

[English]
Delphi XE3 dispose d'une nouvelle fonctionnalité particulièrement intéressante dès lors qu'on veut bien y regarder de plus près. Je veux parler de l'assistance d'enregistrements ("record helper" en anglais).

Malgré son nom qui évoque un type de donnée spécifique, les enregistrements, l'assistance d'enregistrements s'applique aussi à beaucoup de type de données intrinsèques de Delphi dont les ensembles (construction "set of ...").

L'assistance d'enregistrement va nous permettre d'ajouter à un ensemble une ou plusieurs propriétés ou méthode.

L'exemple simple que je vais développer dans cet article concerne l'ajout d'une propriété à un ensemble. Propriété qui va permettre d'initialiser une ensemble depuis un nombre entier, ou d'obtenir un nombre entier à partir d'un ensemble.

Vous me direz : "A quoi cela peut-il bien servir dans le monde réel ?".

C'est simple... J'ai été amené à écrire ce code parce que je travaillais sur un programme qui pilotais une carte d'entrée/sortie (E/S). Cette carte d'E/S dispose de registres dont chaque bit a une signification particulière. Représenter cela en Delphi est assez facile, c'est l'exemple même du cas d'application d'un ensemble : on donne un nom symbolique et parlant à chaque bit; un registre étant finalement un ensemble de bit, et bien nous avons une adéquation parfaite entre la construction "set of" de Delphi et le registre.

Je vais vous faire grâce des détails de ma carte d'E/S et vais basculer sur un exemple plus simple mais tout à fait équivalent. Je vais parler de fruits (les bits du registre) et d'un panier de fruits (Le registre lui-même). Panier spécial puisqu'il peut contenir plusieurs fruits, mais seuelemnt zéro ou un fruit d'un type déterminé.

En Delphi, cela donne:

type
  TFruit  = (frPomme, frPoire, frAbricot, frCerise);
  TFruits = set of TFruit;

Ayant ces deux déclarations, on peut créer des variables et manipuler l'ensemble TFruits:

var
  Panier : TFruits;
begin
  Panier := [frCerise, frPoire];
  Panier := Panier + [frPomme];
  if frPoire in Panier then
    Memo1.Lines.Add('Il y a une poire dans le panier');
end;


Tout cela, c'est très bien et classique, cela existe depuis toujours en Pascal et vous trouverez de nombreux articles qui expliquent en long et en large la manipulation des ensembles en Pascal.

Mais Delphi XE3 fait bien plus que cela ! Vous pouvez ajouter des methodes et propriétés aux ensembles. Voyons comment sur le cas concret que j'évoquais plus haut: transformer l'ensemble en un nombre entier et vice versa.

Disons-le d'emblée: il y a plusieurs manières de réaliser cette tâche de conversion. Je vais vous présenter celle qui est la plus respectueuse du langage et qui ne fait aucune supposition sur la manière dont les ensembles sont réprésentés en interne par le compilateur.

Ce que je veux pouvoir faire, c'est écire ce genre de code:

var
  Panier : TFruits;
  N      : Integer;
begin
  Panier := [frCerise, frPoire];
  // Conversion vers un entier
  N := Panier.Integer;
  // Conversion depuis un entier
  Panier.Integer := 5;
end;

Remarquez la notation: "Panier.Integer". C'est comme si la variable Panier était une classe et que cette classe avait une propriété "Integer". Mais Panier n'est pas une classe, c'est un ensemble ("Set of").

Voici comment il faut procéder:

type
  TFruitsHelper = record helper for TFruits
  strict private
    function  ToInteger : Integer;
    procedure FromInteger(Value : Integer);
  public
    property Integer : Integer read  ToInteger
                               write FromInteger;
  end;

implementation

function TFruitsHelper.ToInteger : Integer;
var
  F : TFruit;
begin
  Result := 0;
  for F := Low(TFruit) to High(TFruit) do begin
    if F in Self then
      Result := Result or (1 shl Ord(F));
  end;
end;


procedure TFruitsHelper.FromInteger(Value: Integer);
var
    F : TFruit;
begin
    Self := [];
    for F := Low(TFruit) to High(TFruit) do begin
        if (Value and (1 shl Ord(F))) <> 0 then
            Self := Self + [F];
    end;
end;


Et c'est tout...
Le code de conversion associe à chaque élément du type énuméré TFruit un bit dans l'entier. Pour cela, j'utilise
  1. Une boucle for F := Low(TFruit) to High(TFruit) do qui permet de parcourir la liste de l'énumération sans spécifier  aucun nom particulier;
  2. La fonction Ord() qui retourne l'ordre de l'élément dans la liste du type énuméré (frPomme vaut 0, frPoire vaut 1, et ainsi de suite);
  3. L'opérateur shl qui permet de décaler un nombre à gauche d'un certain nombre de positions. Le nombre que je décale est 1 et la position est l'ordre de l'élément. Il en résulte un bit à 1 à la position donnée par l'odre.
  4. L'opérateur in qui permet de savoir un des éléments de l'énumération est ou non présent dans l'ensemble.
  5. L'opérateur and qui me permet de masquer un bit dans le nombre entier pour savoir s'il est à zéro ou pas.
Et voilà...

Notes:
  1. Ce code ne fonctionne que si l'ensemble est suffisament petit pour tenir dans un nombre entier (32 bits). Il est facilement modifiable pour fonctionner avec un nombre de 64 bits. Mon objectif initial était de représenter des registres d'une carte d'entrées/sorties, donc aucun soucis car les registres sont généralement de 8, 16 ou 32 bits.
  2. En interne, le compilateur Delphi utilise déjà un bit pour chaque élément d'un ensemble. Sachant cela, il est possible de simplifier le code, mais à ce moment il devient dépendant de l'implémentation de Delphi, ce qui pourrait changer à l'avenir. Voici à quoi pourrait ressembler le code dans ce cas:
      function TFruitsHelper.ToInteger: Integer;
      begin

      {$IF SizeOf(TFruits) = SizeOf(Byte)}
          Result := PByte(@Self)^;
      {$ELSEIF SizeOf(TFruits) = SizeOf(Word)}

          Result := PWord(@Self)^;
      {$ELSEIF SizeOf(TFruits) = SizeOf(Integer)}
          Result := PInteger(@Self)^;
      {$ELSE}
             {$MESSAGE FATAL 'TFruits cannot be represented as an integer'}
      {$IFEND}

      end;

--
François Piette
Embarcadero MVP
http://www.overbyte.be









 

September 4, 2012

Ca y est : Delphi XE3 est disponible !


On en parle depuis un certain temps et nous y voilà : Delphi XE3 est disponible ! Et aussi bien entendu RAD Studio XE3.

Embarcadero a publié les détails du produit.

Régalez-vous !

August 26, 2012

RAD Studio XE3 arrive !


Ca y est, RAD Studio XE3 nous arrive ! L'attente devient insoutenable :-)

Bon d'accord, beaucoup de choses ont déjà filtré. On nous promet de belles choses.

Regardez donc la vidéo de preview

Cette vidéo ne fait qu'égratigner la couche des nouveautés. Il y a plein d'autres choses qu'on ne voit pas. Il est encore un peu tôt pour que je puisse vous en parler, mais cela va venir...

Ceci dit, ce qui m'intéresse particulièrement c'est FireMonkey 2 - alias FM2. Non que je veuille particulièrement faire des applications Windows et les porter sur MAC, mais simplement des application Windows utilisant les possibilités 3D de FM2: En effet, une de mes préoccupations du moment est dans le domaine industriel où je développe des programmes de contrôle de "manipulateur". Ces manipulateurs sont des sortes de robots qui manipulent des pièces pour en faire une inspection RX automatisée. C'est pationnant !

Delphi est un outil merveilleux aussi pour ce genre de programmes.

Je rève donc d'ajouter à mes programmes de contrôle une vue 3D du manipulateur, des pièces et du système RX. Cela apportera à l'utilisateur un réel plus par rapport au 2D d'aujourd'hui. FireMonkey peut faire cela ! Et sans trop de complexité...

Ce qui m'intéresse aussi, c'est la possibilité de faire des applications mobiles, en particulier sur des tabletttes. Mettre à profit mon expérience Delphi Windows me permettra de rapidement produire des application sur ces tablettes.