Saturday, March 9, 2019

Delphi Tip of the Day - Forward Declarations. Solving the Chicken or the Egg Problem.

Today's Delphi Tip of the Day is about resolving "which came first, the chicken or the egg". Because Delphi is a strongly typed language you can't refer to something until it's been defined. However, there are situations where you want to refer to something NOW, but unfortunately it hasn't been defined yet.

In other words "I want the egg of that chicken. I know that chicken hasn't laid that egg yet. However, since I know that chicken is going to lay that egg (trust me I know), let me have that egg now."

This is where forward declarations come into play.

I was brought here by something that came up while reading Pawel Glowacki's book Expert Delphi. I'm on page 233 from Chapter 7 - Sensing the world. Pawel discusses the TSensorManager class in the System.Sensors unit.

There is main TSensorManager class that acts as a gateway to all sensor information. It has a Current: TSensorManager class property that is used to reference all sensor information. At the top of the System.Sensors unit, you can find a TSensoryCategory enumerated type that provides the top level categorization of all possible sensors: 

At this point my head was about to explode so I decided to open the System.Sensors unit and have a look at the actual code Pawel was referring to. Then it hit me.

What the hell is this empty class doing here? How can you have an empty class?
TSensorManager = class;
I remember going down this empty class learning path a while ago. But do you think I can remember what it was, or means. No. Obviously, I had not associated a nice, simple "word picture" to the meaning of this empty class. Because if I had, I would have remembered.

So now I have the nice, easy to remember, "Oh that's Delphi's way of resolving the chicken or the egg thing" word picture.

Have a look at this type definition snippet from the System.Sensors unit:"

  //other type declarations
  TSensorManager = class; //←-- egg reference
  TCustomSensor = class abstract
    public type
      TProperty = (UniqueID, Manufacturer, Model, SerialNo, Name, Description);
      FOnDataChanged: TNotifyEvent;
      FOnSensorRemoved: TNotifyEvent;
      FOnStateChanged: TNotifyEvent;
      FManager: TSensorManager; //←-- egg reference
      FStarted: Boolean;
    // other class definitions
  //other type declarations

  TSensorManager = class abstract  //←--- chicken reference
  public type
    TFilters = TDictionary‹string tsensorfilter=""›;
  private class var
    FCurrentManager: TSensorManager;
    class function InternalGetSensorManager: TSensorManager; static;
    class constructor Create;
    class destructor Destroy;
  protected type
    TSensorManagerType = class of TSensorManager;
    // other class definitions

  //other type declarations

The problem arises because there's a private field declaration in the TCustomerSensor class that references the TSensorManager class that doesn't exist yet. It's the damn "chicken or the egg" thing.

To get around this Delphi allows what is called a forward declaration.

This is not by any means the complete class definition. The complete class definition must be declared somewhere inside the same type declarations where the forward declaration resides.

Here are a couple useful links:

Special Characters:
The  "‹" and "›" in the source code sections were created using the Alt + Keypad method.

Semper Fi
Gunny Mike

Monday, March 4, 2019

Delphi Tip of the Day - What is the "A" prefix I see used on parameters?

Today's Delphi Tip of the Day is all about consistent naming conventions. Consistency in the Delphi and Object Pascal language makes it easier to read and comprehend the code.

I have often wondered why the "A" prefix is used on Delphi parameters. Instead of just accepting it as some esoteric thing as I have done for the past twenty years, I googled around and found an answer. Have a look at the following code snippet:
constructor TPerson.Create(AFirstName, ALastName: string);
  FirstName := AFirstName;
  LastName  := ALastName;
The "A" in "AFirstName" and "ALastName" denotes an Argument as in the above constructor code example.

Typical naming conventions that are used are:
A := Argument
F := Field
T := Type
E := Exception
I := Interface
L := Local Variable
G := Global Variable
The key is to be consistent in all your code. If we as a Delphi community are consistent then it makes it much easier to communicate with each other.

See also:
Object Pascal Style Guide By: Charles Calvert

#delphi #tipoftheday #capecodgunny

Gunny Mike