Jump to content
Willicious

Undeclared identifier errors are something I could do with knowing how to deal with once and for all

Recommended Posts

I get undeclared identifiers almost every time I try to implement something in the project I'm working on. Here's an example:

 

I'm trying to check the state of a condition called GameSpeed, which is defined in the unit GameWindow. In the unit LemGame, if I try to add "GameWindow" under "uses" at the top of the unit, I get a circular reference error. It's previously been explained to me that when this happens, I should list the unit under "implementation > uses" instead.

 

Here's what I currently have (edited to fit it here😞

 

unit LemGame

implementation

uses
  GameWindow;

procedure TGame.Start(aReplay: Boolean = False);
begin
  if GameSpeed = gspRewind then
  begin
    GameSpeed := gspNormal;
  end;
end;

The idea is to return the game's speed to Normal forward-speed time when the Rewind speed has gone all the way to the start. TGame.Start is a procedure which deals with all conditions present at the start of the game, so this seems the best place to get it to check for the GameSpeed condition.

 

However, GameSpeed gets an undeclared identifier error despite being present in the GameWindow unit. I've also tried changing it to TGameSpeed, but nothing doing.

 

In GameWindow, GameSpeed is a property which is set in this way:

 

function GetGameSpeed: TGameSpeed;

property GameSpeed: TGameSpeed read GetGameSpeed write SetGameSpeed;

function TGameWindow.GetGameSpeed: TGameSpeed;
begin
  Result := fGameSpeed;
end;

Everywhere in GameWindow that I use "if GameSpeed = {whatever}", I get no errors.

 

Do I therefore need to copy-paste all of the stuff regarding GameSpeed into the LemGame unit? Because I thought the point of being able to call on stuff from other units is that you don't have to redefine things again.

 

Very confused.

 

Maybe there is some information I haven't included here which might help to answer this. If so, I apologise in advance and I'll do my best to provide whatever is needed. The Rewind feature has been hanging around unfinished for weeks now because of problems like this.

 

Edited by Willicious

Share this post


Link to post

1) "Interface" session ( unit top) is public to another units see all type/var/classes/etc... defined there... here occurs the "circular references"

 

2) "Implementation" session ( where your code is done ) is private for "this unit" ... (others dont see it)

 

3) the "circular references" happens when "unitA" call "unitB", and "unitB" call "unitA", same that INDIRECTLY!!! for example: A call C, B call A,  C call B... but this "USES" is in "Interface" session. in "Implementation" session this dont occurs, because it's "privative" of each unit... you see?

 

4) you can only see, in others units,  type/var/classes that it's defined on "Interface" session of each unit.

 

5) if a class, use any "private Type" into this class, then, you needs to do reference to the "class" that define the others types/class/fields/etc... you see?

 

 

Edited by programmerdelphi2k

Share this post


Link to post

"Circular Reference" example:

unit Unit1;

interface
  uses // < ---- WARNING
    Unit3;   //  Unit1 uses Unit3 --> Unit3 uses Unit2  --> Unit2 uses Unit1

// code...

implementation

{$R *.dfm}

// uses  Unit3;  // <---- usage OK

end.

 

unit Unit2;

interface  // < ---- WARNING
   uses Unit1;   // Unit1 uses Unit3 --> Unit3 uses Unit2  --> Unit2 uses Unit1 <---- WONT COMPILE

// code...

implementation

//uses  Unit1;  // <---- usage OK

end.

 

unit Unit3;

interface // < ---- WARNING 
  uses Unit2;   //  Unit1 uses Unit3 --> Unit3 uses Unit2  --> Unit2 uses Unit1

// code...

implementation

// uses  Unit2;  // <---- usage OK

end.

 

Share this post


Link to post


 

unit uGameWindow;

interface

type
  TEnumGameSpeed = (gsOne, gsTwo, gsThree);

  TClassGameSpeed = class
  private
    FGameSpeed: TEnumGameSpeed; // store the current value
    //
    function GetGameSpeed: TEnumGameSpeed;               // get value
    procedure SetGameSpeed(const Value: TEnumGameSpeed); // set value
  public
    // allow change or review value
    property GameSpeed: TEnumGameSpeed read GetGameSpeed write SetGameSpeed;
  end;

implementation

{ TClassGameSpeed }

function TClassGameSpeed.GetGameSpeed: TEnumGameSpeed;
begin
  result := FGameSpeed;
end;

procedure TClassGameSpeed.SetGameSpeed(const Value: TEnumGameSpeed);
begin
  FGameSpeed := Value;
end;

end.

 

unit uLemGame;

interface

uses
  uGameWindow; // necessary here because the FGameSpeed field on class!

type
  TClassGame = class
  private
    FGameSpeed: TClassGameSpeed;
  public
    constructor Create;
    destructor Destroy; override;
    //
    procedure Start(AReplay: boolean = false);
  end;

implementation

{ TClassGame }

constructor TClassGame.Create;
begin
  FGameSpeed := TClassGameSpeed.Create; // object created
end;

destructor TClassGame.Destroy;
begin
  FGameSpeed.Free; // object destroyed
  //
  inherited;
end;

procedure TClassGame.Start(AReplay: boolean);
begin
  // object in usage...
  if AReplay then
    FGameSpeed.GameSpeed := TEnumGameSpeed.gsOne
  else
    FGameSpeed.GameSpeed := TEnumGameSpeed.gsTwo;
end;

end.

 

Edited by programmerdelphi2k

Share this post


Link to post
11 hours ago, Willicious said:

I get undeclared identifiers almost every time I try to implement something in the project I'm working on. Here's an example:

 

I'm trying to check the state of a condition called GameSpeed, which is defined in the unit GameWindow. In the unit LemGame, if I try to add "GameWindow" under "uses" at the top of the unit, I get a circular reference error. It's previously been explained to me that when this happens, I should list the unit under "implementation > uses" instead.

 

Here's what I currently have (edited to fit it here😞

 


unit LemGame

implementation

uses
  GameWindow;

procedure TGame.Start(aReplay: Boolean = False);
begin
  if GameSpeed = gspRewind then
  begin
    GameSpeed := gspNormal;
  end;
end;

 

You are not understanding the concept of identifier scope correctly. Gamespeed is a property of the class TGameWindow, which is defined in unit GameWindow. To access it in your TGame class this class has to derive from TGameWindow (in which case is can access all members of TGameWindow that are not declared private) or you need an instance of TGameWindow accessible from TGame, in which case you have  to change the code to qualify Gamespeed with that instances name, e.g. FWindow.Gamespeed, if FWindow is the variable/field containing the TGameWindow instance.

 

If you want all instances of the TGameWindow class to share the same Gamespeed value you can change the property into a class property and the corresponding field and accessor methods to class field and class methods. In this case you can use the syntax TGameWindow.Gamespeed in TGame.Start to access the property.

Share this post


Link to post

Thanks @PeterBelow for the explanation, I think I understand what I'm doing wrong here, your explanation has helped the penny to drop.

 

This probably isn't the best way to achieve what I'm trying to do, since LemGame otherwise doesn't need to be aware of the existence (or not) of GameWindow. It seems a bit drastic to make big changes to the existing structure to achieve something relatively small.

 

I'm re-focusing my efforts on the GameWindow unit. I need to find a way for the Rewind code itself to check for reaching the start of the level. But, I have tried several ways of doing this to no good result. I'll persevere with it, though; perhaps something useful will present itself if I keep looking.

Edited by Willicious

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×