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:"
type

  //
  //other type declarations
  //
  
  TSensorManager = class; //←-- egg reference
  
  TCustomSensor = class abstract
    public type
      TProperty = (UniqueID, Manufacturer, Model, SerialNo, Name, Description);
    private
      FOnDataChanged: TNotifyEvent;
      FOnSensorRemoved: TNotifyEvent;
      FOnStateChanged: TNotifyEvent;
      FManager: TSensorManager; //←-- egg reference
      FStarted: Boolean;
    //
    // other class definitions
    //
  end;
  
  //
  //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
    //
  end;

    
  //
  //other type declarations
  //

 implementation
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:

http://docwiki.embarcadero.com/RADStudio/Rio/en/Classes_and_Objects_(Delphi)#Forward_Declarations_and_Mutually_Dependent_Classes

http://www.delphibasics.co.uk/RTL.asp?Name=Class

Special Characters:
The  "‹" and "›" in the source code sections were created using the Alt + Keypad method.
https://www.keynotesupport.com/websites/special-characters-symbols.shtml


Enjoy!
Semper Fi
Gunny Mike
https://zilchworks.com

No comments:

Post a Comment