Willicious 8 Posted April 2, 2023 (edited) 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 April 2, 2023 by Willicious Share this post Link to post
programmerdelphi2k 237 Posted April 2, 2023 (edited) 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 April 2, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted April 2, 2023 "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
programmerdelphi2k 237 Posted April 2, 2023 (edited) 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 April 2, 2023 by programmerdelphi2k Share this post Link to post
PeterBelow 238 Posted April 2, 2023 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
Willicious 8 Posted April 2, 2023 (edited) 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 April 2, 2023 by Willicious Share this post Link to post