Willicious 8 Posted May 25, 2023 (edited) EDIT: Apologies for the rant-like nature of this post, but... I'm getting frustrated with Delphi programming. Rather than toss my laptop out the window, I'd rather express my frustration here on the Forums and hopefully it comes to something a bit more productive than a broken laptop and a feeling of regret. ----- Why can I sometimes access properties, functions, variables etc from other units, and sometimes not? "Private" and "public" doesn't seem to make any difference, either. Sometimes I'll start to type the name of a class, followed by "." and a list of all the available items comes up. I assure you, sometimes even when I make something "public", it still won't appear in that list. Why??? Also, the practice of creating "instances" of classes within procedures is ridiclously cumbersome and unintuitive. If I've created a property/variable/field/whatever in Class1.pas, it should be accessible from Class2.pas without me having to create a "version" of Class1 first. If it's declared in uses, that should be that! Forgive me for the rant, but aren't we as programmers supposed to be in charge of the program, not the other way around?! Edited May 25, 2023 by Willicious Share this post Link to post
chkaufmann 17 Posted May 25, 2023 private and protected are "friends" for classes within the same unit. If you don't want that, use "strict private" and "strict protected". For your second problem, you should provide an example. It is not clear to me what doesn't work. And by the way: When you write questions like this in a forum where most of the answers come from volunteers, you probably end up on the ignore list of many of them. Regards Christian 1 Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 9 minutes ago, Willicious said: we as programmers supposed to be in charge of the program, for sure, BUT when you dont "declare" the objects/var/procedure/etc.. you DONT BE! right? then, for this, you NEED declare all that you need use! 1 Share this post Link to post
Willicious 8 Posted May 25, 2023 (edited) In fairness, I said it was a rant, not a question. I think Delphi is a very poorly structured programming language which very quickly renders programs impenetrable to anyone other than the original developer, who may even end up struggling with it if they've forgotten what is supposed to be accessible from where. Why not just have everything dynamically accessible from everywhere else? Why even the need for "private" and "public" (which, as I said in the OP, doesn't always make a difference). Yes, other languages have this going on as well but nowhere else have I struggled with it as much as with Delphi/Pascal. I'm constantly running into problems that largely involve wrestling with the structure of units & classes. Here's an example: I have a unit called "Replay.pas" which deals with saving, loading and interpreting data from text-based game replay files. Then, another unit called "ControlPanel.pas" which deals with the in-game controls and how they relate to what's happening in the game. I made a public property in the TReplay class which I need to call from ControlPanel. Even though Replay is declared in uses within ControlPanel, I can not call the property. It's brought weeks of progress to a grinding halt, and it's frustrating, hence the rant. Edited May 25, 2023 by Willicious Share this post Link to post
David Heffernan 2345 Posted May 25, 2023 9 minutes ago, Willicious said: I said it was a rant, not a question. I think Delphi is a very poorly structured programming language which very quickly renders programs impenetrable to anyone other than the original developer, who may even end up struggling with it if they've forgotten what is supposed to be accessible from where. It's possible to write poor code in any language. 3 Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 @Willicious I think your dissatisfaction is leading you to discuss a rhetoric that has existed as long as programming has existed! As for your dissatisfaction with your class, I think it only proves that it is not consistent and has been wrongly constructed, and possibly being misunderstood by you! Better rethink your code! As for using everything anywhere, then give up on object-oriented programming languages, or close to it. Better go back to "Basic for MSX"... lots of bits and bytes, no predefined structure, go forward or backward as you wish! And at the end, save your code on cassette tapes! If you want, try to post your code here or in some repository that, surely, someone will be able to give you a hand! 1 Share this post Link to post
Willicious 8 Posted May 25, 2023 (edited) OK, OK, I get it. I'm the problem, not the programming language. All praise the wonderful and mighty programming language that is Delphi (or, indeed, any other)! But seriously, I can spend hours on this thing and achieve so little because of problems with the code structure. I appreciate that people on here are there to help, that's a good thing. ChatGPT is also a decent tool, but it's by no means perfect. Regarding examples/context... it's difficult to provide when the context happens to be thousands and thousands of lines of code sprawling dozens of .pas unit files, none of which I created myself. Edited May 25, 2023 by Willicious Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 @Willicious try this sample about class Scope that I did a long time ago... Class_Scope_Visibility.7z 1 Share this post Link to post
Willicious 8 Posted May 25, 2023 (edited) OK, I managed to get it working. I needed to declare an instance of the class in the new unit, not just declare the unit itself. For future reference, and for anyone else struggling witn this: The new instance needs to be declared at the top of the page in the current class (i.e. the one in which you want to use the instance). Like this: unit TheCurrentUnit type class TTheCurrentClass private/public (doesn't matter whether it's private or public, but public means you should in theory be able to access it elsewhere) fDeclareHereAndCallItWhateverYouWant: TTheNameOfTheOriginClass; property DeclareHereAndCallItWhateverYouWant: TReplay read fDeclareHereAndCallItWhateverYouWant; procedure TTheCurrentClass.Create; begin fDeclareHereAndCallItWhateverYouWant = TTheNameOfTheOriginClass.Create; end; procedure TTheCurrentClass.Destroy; begin fDeclareHereAndCallItWhateverYouWant.Free; end; By following the above, an instance of the class has been created in the unit which can then, in theory, be called to make use of the properties from the other unit. I'm not entirely sure why it's sometimes necessary to do this; in other examples I've seen, it's enough just to declare the unit under "implementation / uses". But, if that fails, try the example here. Edited May 25, 2023 by Willicious Share this post Link to post
Brandon Staggs 260 Posted May 25, 2023 1 hour ago, Willicious said: I said it was a rant, not a question. I think Delphi is a very poorly structured programming language which very quickly renders programs impenetrable to anyone other than the original developer, who may even end up struggling with it if they've forgotten what is supposed to be accessible from where. Delph is a strongly-typed language with well-defined and understood private, protected, and public clauses of classes. Other than getting used to the "friend class" behavior of classes defined in the same unit, I can't imagine what is so mysterious about it. My guess is that you are confused due to the "friend class" behavior you have seen in some work and didn't realize what was happening. You can certainly come across code that is "impenetrable to anyone other than the original developer," but that has little to do with Delphi, at least not in the areas you have expressed frustration over in this thread. 1 Share this post Link to post
Willicious 8 Posted May 25, 2023 (edited) 5 minutes ago, Brandon Staggs said: Other than getting used to the "friend class" behavior of classes defined in the same unit, I can't imagine what is so mysterious about it It's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why do I have to redefine/redeclare it in a different unit? Why not this instead: Unit 1 Class1 PropertyA Unit 2 uses Class1 if Class1.PropertyA etc... Anyway, never mind. I've already accepted it's a "me" problem. I seemed doomed to forever see ways in which things could be better rather than actually getting to know and use them as they are. Edited May 25, 2023 by Willicious Share this post Link to post
Brandon Staggs 260 Posted May 25, 2023 (edited) 2 minutes ago, Willicious said: It's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why do I have to redefine/redeclare/re-whatever it in a different unit? Why not this instead: Unit 1 Class1 PropertyA Unit 2 uses Class1 if Class1.PropertyA etc... The uses clause is not for classes, it's for units. The only "visibility" issue with regard to CLASSES defined in UNITS is what you put in INTERFACE vs IMPLEMENTATION. But since you are refusing to post ACTUAL CODE (nothing you have posted would compile, so it can't be ACTUAL CODE), there is little anyone here can do to help you understand. Edited May 25, 2023 by Brandon Staggs 4 Share this post Link to post
Attila Kovacs 627 Posted May 25, 2023 It's unclear if you have OOP problem or the IDE is not working properly. Share this post Link to post
David Heffernan 2345 Posted May 25, 2023 32 minutes ago, Willicious said: Anyway, never mind. I've already accepted it's a "me" problem. I seemed doomed to forever see ways in which things could be better rather than actually getting to know and use them as they are. To be fair, I think it's only when one has a comprehensive understanding can one take an informed critical view. I think there are plenty of design flaws with the language, but I don't think that they are holding you back yet. Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 1 hour ago, Willicious said: It's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why do I have to redefine/redeclare it in a different unit? Why not this instead: the key-words is: "instances-of-classes" when you define the structure of a class, in fact, you are just defining a "layout" of future objects that will be created in memory. To take advantage of this structure, you need to create the object in memory. Or simply "instantiate the object"! For this reason, it is not possible to perform any actions on the object without first creating it! 1 Share this post Link to post
Willicious 8 Posted May 25, 2023 2 hours ago, Brandon Staggs said: since you are refusing to post ACTUAL CODE (nothing you have posted would compile, so it can't be ACTUAL CODE), there is little anyone here can do to help you understand Yes, but surely we can use pseudo-code or non-compilable examples to illustrate the concepts? The only reason I'm reluctant to post actual code is because I would literally have to post multiple units-worth of code in order for anybody to be able to help. So much of what's going on is very program-specific, multi-faceted and convoluted. I've had help elsewhere on this Forum which would have worked in a newly-created codebase, for example, but which I can't do much with when I have to crowbar it in to what I'm currently dealing with. The program is a clone of the "Lemmings" game from 1991, but with modern QOL features (such as replay functionality, skill projections, etc). I'm more than happy to share the repo if anybody is interested in helping me out with it - it's an open-source project anyway. I could really do an with experienced pair of eyes and someone to help turn ideas into reality. I've already done a lot by myself, but I do continually come up against the types of problems that a seasoned Delphi expert would be able to recognise and troubleshoot quite quickly. I've even considered paying for lessons tbh, such as the difficulty of this project sometimes gets! Share this post Link to post
Willicious 8 Posted May 25, 2023 (edited) Basically, everything I try (and fail) to do comes down to undeclared identifiers, not being able to call procedures/functions/properties/variables from other classes. It's always the thing that holds me back. All the ideas are there, and should work. Here's an example: function TReplay.GetRRChangeOnCurrentFrame: Boolean; var i: Integer; begin Result := False; for i := 0 to fSpawnIntervalChanges.Count -1 do begin if TReplayChangeSpawnInterval(fSpawnIntervalChanges[i]).Frame = LemGame.CurrentIteration then begin Result := True; OutputDebugString(PChar('SpawnIntervalChangeDetected')); Exit; end; end; end; In this code, the call for LemGame.CurrentIteration generates an undeclared identifier error. This is in spite of LemGame being one of the uses in this unit, and CurrentIteration being public and callable from other units (elsewhere, it's called as Game.CurrentIteration, but that doesn't work here either). All I want to do is return this value to True if the Spawn Interval has changed on the current frame. All the code is there, the ideas work in theory, but for some reason I just..... can't use this bit of the code in this particular place! Edited May 25, 2023 by Willicious Share this post Link to post
programmerdelphi2k 237 Posted May 25, 2023 (edited) 16 minutes ago, Willicious said: LemGame. where is this object (class/interface/record) created ? in the same unit or where? 16 minutes ago, Willicious said: fSpawnIntervalChanges[i] this returns really a "TReplayChangeSpawnInterval", or what other type? Edited May 25, 2023 by programmerdelphi2k Share this post Link to post
Willicious 8 Posted May 26, 2023 54 minutes ago, programmerdelphi2k said: where is this object (class/interface/record) created ? in the same unit or where? LemGame is a different unit, it contains a (variable? property? field?) called CurrentIteration which, essentially, represents the current in-game frame. Share this post Link to post
Willicious 8 Posted May 26, 2023 (edited) For what it's worth, I've managed to find a solution to the particular problem that's been bothering me today. The answer was in the existing code, I just needed to find the correct function, and the correct way to call it in another unit. However, any advice on calling properties/functions/procedures/fields/etc from another class/unit would be invaluable. I'm willing to learn! Thanks for your patience, everyone. Apologies again for the outpouring of frustration. Edited May 26, 2023 by Willicious Share this post Link to post
Joseph MItzen 250 Posted May 26, 2023 7 hours ago, programmerdelphi2k said: @Willicious As for using everything anywhere, then give up on object-oriented programming languages, or close to it. Better go back to "Basic for MSX"... lots of bits and bytes, no predefined structure, go forward or backward as you wish! And at the end, save your code on cassette tapes! Python is more object-oriented than Delphi (EVERYTHING is an object - types, functions, classes, even numbers) and there is no enforced "private" setting. Privacy is only by naming convention as a guideline. And you don't even need to save its code to a cassette tape! 🙂 There's basically two schools of thought in programming language design - that you have to hide and control everything to "protect" users or developers or the system, and the other school is about giving power to the developers to do whatever they wish and put responsibility for doing it properly and safely on the developer, not the language/compiler. I believe Guido Van Rossum, the creator of Python, dubbed the latter design view the "we're all adults here" philosophy. Which view has more merit is up to designers and developers to decide for themselves, but it's clearly possible to design object-oriented features into languages using either approach. And on a marginally-related note, the Internet Archive had a great write-up recently about programs stored on cassette tape... 🙂 https://blog.archive.org/2023/05/02/the-easy-roll-and-slow-burn-of-cassette-based-software/ 1 Share this post Link to post
Virgo 18 Posted May 26, 2023 9 hours ago, Willicious said: t's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why do I have to redefine/redeclare it in a different unit? Why not this instead: Unit 1 Class1 PropertyA Unit 2 uses Class1 if Class1.PropertyA etc... What kind of programming languages have you actually used before? Because you would have same issues in most languages... Event in javascript or python you need an instance to actually use it. 1 Share this post Link to post
Dalija Prasnikar 1392 Posted May 26, 2023 (edited) 5 hours ago, Willicious said: LemGame is a different unit, it contains a (variable? property? field?) called CurrentIteration which, essentially, represents the current in-game frame. Without seeing where LemGame is declared and how it is impossible to say why you have an error. It is also important to note that sometimes you may get error in the IDE (read squiggly lines) in some perfectly valid code. You should always try to compile code to see real errors. also, when you do get compiler error, you should fix them in order they appear as the first error can cause subsequent issues for the compiler and it is possible that compiler will show more errors in otherwise valid code. But, it seems like your main problem is not with Delphi but with OOP principles in general as you are trying to access properties in a class without creating instance of a class. Class is just a template from which object instances are created. Before you create object instance data (class fields and properties) don't exist in memory, so there is nothing you can access there. The exception to that are class fields, properties and methods that are additionally marked with class descriptor. Those can be accessed directly. They are basically global variables that are organized under class namespace. See https://docwiki.embarcadero.com/RADStudio/Sydney/en/Properties_(Delphi)#Class_Properties Purpose behind creating object instances from class before using them is that most of the time you will need more than one. For instance, you can have one global object instance of a game, but you will need more instances of game characters or other game objects. Think of it, class gives you description about attributes and functionality of a person, and when you construct object instances of that class you will get separate persons: Joe, Mike, Alice... I would advise you to read a bit about Delphi language and some basic concepts. good place to start is https://learndelphi.org/ and Delphi Language Guide https://docwiki.embarcadero.com/RADStudio/Sydney/en/Delphi_Language_Reference Edited May 26, 2023 by Dalija Prasnikar 1 Share this post Link to post
PeaShooter_OMO 10 Posted May 26, 2023 9 hours ago, Willicious said: It's the idea of instances-of-classes that keeps catching me out. If a class is defined somewhere, it should be callable as that class. Why? Do you also expect to move all your furniture into the blueprint of a house? No, you would use the house's blueprint to build one or multiple instances of an actual house, then you would move into one of them. The class declaration and all its related code is the blueprint and the instance is the actual house. You design a class and then when you need to do something with that class you would create an instance of it and use it. Remember to at least free/destroy that instance. You are the only one living in it and nobody else will buy your vacant spot and vacant spots lead to bad neighbourhoods (memory leaks). 9 hours ago, Willicious said: Unit 1 Class1 PropertyA Unit 2 uses Class1 if Class1.PropertyA etc... There are ways to do the above but I don't want to send you down a dark hole. Get to know Delphi and basic Objects/Classes a bit better first and learn to manage the objects properly first. About the game and its development; Whether you were recruited to do development on this project or whether you got involved voluntarily it will be the same... it seems you don't understand Delphi principles yet and your approach to the problem and how you reacted here was not top-notch. I follow the philosophy that it is always my fault if something does not work out the way I wanted in the code I am involved with unless it is proven otherwise. It helps me to feel responsible for it and forces me to investigate properly and whats even cooler is that I just usually fix the issue even if someone else is to blame for it. It saves time and I just learned a whole bucket load and it sharpens the most critical development skill anyone can posess: Debugging. Learn to appreciate source code. If you don't know how something works then go and read the source code. You will get the understanding for it and you will learn again. I suspect most guys here might have had some idea in their heads that made them cringe on working on a project along with another coder that attacked the tools first instead of trying to understand first. If you want people to join the project or lend a hand then the environment for the collaboration should be inviting and free of nonsense. Share this post Link to post
Stano 143 Posted May 26, 2023 (edited) 8 hours ago, Willicious said: if TReplayChangeSpawnInterval(fSpawnIntervalChanges ).Frame = LemGame.CurrentIteration then I was very struck by this line. Why do you have retyping there? What happens if fSpawnIntervalChanges) is not TReplayChangeSpawnInterval? Also, I don't like the comparison character = there These are very gross, severe errors in the code!!! And do see my signature! Edited May 26, 2023 by Stano Share this post Link to post