SgtZdog 1 Posted June 8, 2023 (edited) Hey all, I'm a rather new to Delphi (a few years programming in C#, C++, Python, and Clojure). (A note, the original thought that brought up this question is abandoned as I thought of a somewhat better solution for when I was originally writing this question, but the core question still seems good to have an answer for,) I'm trying to have a static method in a child class access protected members declared in the parent, but having trouble figuring out how to do it. Is it not possible or am I doing it wrong? Simplified code example: edit: I tried making the method I want to use owned by the child class instead (parent class doesn't particularly need to own it anyway), but that doesn't seem to work either. unit1.pas: interface type TParentClass = class protected function ProtectedMember(); Boolean; virtual; abstract; unit2.pas interface type ChildClass = class(ParentClass) public //Question 1) class function Maker() : ChildClass; //Question 2) (I haven't tested this, so maybe this works no problem?) class procedure Manipulator(instance : ParentClass); //Question 3) (I haven't tested this, so maybe this works no problem?) procedure Stuffer(instance2 : ParentClass); protected procedure ChildOwnedMethod(); end; implementation class function ChildClass.Maker() : ChildClass; begin Result := ChildClass.Create(); //Edit: No longer incorrectly use inherited Result.ProtectedMember(); //Trying to do this and it doesn't seem to work Result.ChildOwnedMethod(); //Bonus! I tried doing this instead and it doesn't seem to work either. end; class procedure ChildClass.Manipulator(instance : ParentClass); begin instance.ProtectedMember(); //Does this work? Is there a way to do it? end; procedure ChildClass.Stuffer(instance2 : ParentClass); begin ProtectedMember(); //I would expect this to work instance2.ProtectedMember(); //Does this work though? Is there a way to do it? end; procedure ChildClass.ChildOwnedMethod(); begin //Do Stuff end; Edited June 9, 2023 by SgtZdog additional question to add Share this post Link to post
programmerdelphi2k 237 Posted June 8, 2023 (edited) @SgtZdog ParentClass: it's a class like any other! no problem! BUT, it has a "virtual/abstract" method!!! --- so this says that the "subclasses" must "mandatory" implement it, otherwise you'll have an access violation when in use!!! --- but fortunately, (should) an exception will be created during the usage of "ChildClass", showing the "abstract method" never implemented in the subclasses!!! In general, I think it's not right for your classes, because "ChildClass" intends to use a method defined in "ParentClass", but "ParentClass" doesn't implement that method and requires it to be implemented by subclasses... I think we have a " impasse" here no? Now, if the "subclasses" implement this method, "it" does not belong to the "ParentClass", but to the "ChildClasses"!!! Another problem to take into account is mixing "common method" with "class method". Remembering that the "class methods" belong to the class, and will be accessible to any number of instances of objects created from the "Childclass". That is, they can be read, written, etc... by any objects of this "ChildClass" hierarchy. Another factor, too, is the accessibility between "common methods" and "class methods", that is, the conversation between them, internally! unit uParentClass; interface type TParentClass = class protected // a "common method" function MethodWhatHappensHere: string; virtual; abstract; // it will be implemented on subclasses! end; implementation end. // ------------ unit uChildClass ----------------- interface uses uParentClass; type TChilClassUsingInheriting = class(TParentClass) protected // it's mandatory implement it here! -- uncomment it!!! // function MethodWhatHappensHere: string; override; // implementing... --- E2362 Cannot access protected symbol "xxxxxxxxx" directly public function ShowSomeMessage: string; // now, it's possible! end; implementation { TChilClassUsingInheriting } function TChilClassUsingInheriting.ShowSomeMessage: string; begin // if "MethodWhatHappensHere" was not defined in this subclass, then the "ancestral" will be used anyway! // result := {inherited} MethodWhatHappensHere; // using "inherited" you call the "ancestral" method, BUT remember: there, it is "abstract"!!! //result := { Self. } MethodWhatHappensHere; // using "Self." it's ok if was defined on "ChildClass"!!! end; // function TChilClassUsingInheriting.MethodWhatHappensHere: string; // begin // result := 'I dont know! from: ' + Self.ClassName; // end; end. // -------- form1 --------- implementation {$R *.dfm} uses uChildClass; procedure TForm1.Button1Click(Sender: TObject); var LChildClass: TChilClassUsingInheriting; begin // Unit1.pas(42): W1020 Constructing instance of 'TChilClassUsingInheriting' containing abstract method 'TParentClass.MethodWhatHappensHere' LChildClass := TChilClassUsingInheriting.Create; try LChildClass.ShowSomeMessage; finally LChildClass.Free; end; end; Edited June 8, 2023 by programmerdelphi2k Share this post Link to post
programmerdelphi2k 237 Posted June 8, 2023 class function ChildClass.Maker() : ChildClass; begin Result := inherited Create(); // calling "ancestral Create" as Child result? ... some sense here? ... end; if "Child" inherit from "Parent", why call "Create from Parent as a result as Child"? if "Maker" is a class method, then why create a instance as Child, if class method does not needs any creation? if was ok, remember: "Parent" has a "abstract method" that needs to be implmented on "Child" confused not? Share this post Link to post
SgtZdog 1 Posted June 9, 2023 Ah, this was a good catch: Quote // calling "ancestral Create" as Child result? ... some sense here? Yeah, that was not intended and was my mistake. I fixed up the original post to do things correctly. Things seem to be working now, thank you very much for your help. To hopefully answer the core of your questions, the intent of the protected abstract method is to enforce child classes having to have it so that a parent method can call it and lean on a correct implementation being available courtesy polymorphism. As I mentioned I've changed tack, so this isn't what I'm going to do, but this was still good practice at Delphi syntax for me. Share this post Link to post
David Schwartz 426 Posted June 9, 2023 static (ie, 'class' methods) cannot access members since they don't refer to any instance of an object. You seem to be conflating a Factory pattern with ... a mess (?) Are you wanting a Singleton here? i can't really tell what you're trying to accomplish. Try explaining it in words first because your code makes no sense to me. 1 Share this post Link to post
programmerdelphi2k 237 Posted June 9, 2023 (edited) @SgtZdog unit uParentClass; interface type TParentClass = class protected // now, this class can to be used as any other... directly or inherited! function PleaseOverrideMeIfWant: string; virtual; // no needs to be overrided on subclasses! end; implementation { TParentClass } function TParentClass.PleaseOverrideMeIfWant: string; begin result := 'hello, im TParentClass definition'; end; end. -------------- child ----------------- unit uChildClass; interface uses uParentClass; type TChildClassInheritingFromParent = class(TParentClass) private function GetPleaseOverrideMeIfWant: string; protected function PleaseOverrideMeIfWant: string; override; // overriding ancestral function... public property ReadMyProtectMethodOverrited : string read PleaseOverrideMeIfWant; property ReadMyProtectMethodNoOverrited: string read GetPleaseOverrideMeIfWant; end; implementation { TChildClassInheritingFromParent } function TChildClassInheritingFromParent.GetPleaseOverrideMeIfWant: string; begin result := inherited PleaseOverrideMeIfWant; // using ancestral... end; function TChildClassInheritingFromParent.PleaseOverrideMeIfWant: string; begin result := 'hello, im TChilClass definition overriding TParentClass definition...'; end; end. ------------------- form1 -------------- implementation {$R *.dfm} uses uChildClass; procedure TForm1.Button1Click(Sender: TObject); var LChilClass: TChildClassInheritingFromParent; begin LChilClass := TChildClassInheritingFromParent.Create; try Memo1.Lines.Add(LChilClass.ReadMyProtectMethodOverrited); // Memo1.Lines.Add(LChilClass.ReadMyProtectMethodNoOverrited); finally LChilClass.Free; end; end; initialization ReportMemoryLeaksOnShutdown := true; end. Edited June 9, 2023 by programmerdelphi2k Share this post Link to post
SgtZdog 1 Posted June 9, 2023 8 hours ago, David Schwartz said: static (ie, 'class' methods) cannot access members since they don't refer to any instance of an object. You seem to be conflating a Factory pattern with ... a mess (?) Are you wanting a Singleton here? i can't really tell what you're trying to accomplish. Try explaining it in words first because your code makes no sense to me. I was indeed going for something like a factory pattern (though simplified in that each child class provides static methods for construction instead of having a separate class for the sole purpose of construction). Rest assured, as mentioned above, I'm reducing a bunch of this. Reason for the factory style idea though was to return nil instead of raising an exception in the case of bad construction. I might still keep the static construction methods, but they will at least avoid the unnecessary polymorphism. The current task I'm working on is overhauling a logging system for a game (these are user facing reports). The messages have quite complicated construction and I'm trying to test the viability of removing the message generating from the game logic (this is beneficial in that it allows us to more easily and dynamically have actions like highlighting given units in the messages). 1 Share this post Link to post
SgtZdog 1 Posted June 9, 2023 Programmer, I definitely thought about virtual, but the current base class has no need for the virtual methods and under my current concept construction of the base class would be replaced by more specific child classes. (I'm assuming your use of properties was just for demonstration purposes and not part of a proposed solution.) Share this post Link to post
programmerdelphi2k 237 Posted June 9, 2023 @SgtZdog of course, my props was more to show, not to use. as I dont know exactly what you expect ... as any log-system I think that a Singleton Pattern as "container" would can be used as your repository of messages, or a Observer Pattern... Share this post Link to post
SgtZdog 1 Posted June 9, 2023 (edited) Yes, there is a separate class not described by my current efforts that holds the messages and deals with displaying them. This is for the messages themselves. So the entire log is held by one class, with each message being an instance of something that inherits from the parent class here. Our legacy system messages were plain strings and then some regex would let us add functionality. I'm experimenting with moving away from these plain strings for speed, ease of use, and capability purposes. MessageClass / \ type1 type2 Log: contains array of MessageClass Edited June 9, 2023 by SgtZdog Share this post Link to post
programmerdelphi2k 237 Posted June 9, 2023 @SgtZdog is possible show the skeleton this class, for curiosity? Share this post Link to post
SgtZdog 1 Posted June 9, 2023 The class is nothing terribly complex, but here's the interface with the core functions (for brevity I'm not including everything like I/O for saving and loading): TMessageLog = class public constructor Create(); destructor Destroy(); override; procedure Clear(); procedure AddEntry(aLogEntry : TMessageLogEntry); function GetLogEntries(reporter : Integer) : TArray<TMessageLogEntry>; overload; function GetLogEntries(reporter : Integer; anUntilTime : TDateTime) : TArray<TMessageLogEntry>; overload; function GetLogEntriesMatchingAnyTag(reporter : Integer; const theTags : TArray<TMessageLogTag>) : TArray<TMessageLogEntry>; overload; end; (I'll note, I am not the original author of this class and it is still subject to change) Share this post Link to post
programmerdelphi2k 237 Posted June 9, 2023 how the message will be processed when received? said, who send? who read? the messages, what object is it? (string/object/etc...) is thread-safe or what? Share this post Link to post
SgtZdog 1 Posted June 9, 2023 (edited) Messages aren't really processed by the log, just held by it, barring the filtering provided by the different GetLogEntries methods. Pretty much anywhere in the application can read/write the log (note that we functionally only have a few places that read from it, but many that write to it). The messages are objects that I'm currently experimenting with using some inheritance to simplify things sort of since we have over 100 different possible message formats. The application is broadly speaking single-threaded and not thread safe. edit: The log is a global right now, and fixing it up to be singleton is probably on my todo list. Edited June 9, 2023 by SgtZdog Share this post Link to post
programmerdelphi2k 237 Posted June 9, 2023 on msg, a simple sample using Singleton project Share this post Link to post