The problem is that whatever solution I use cannot have any kind of "hard-coding". If I were, for example, to have overloaded methods for adding objects to the object list that added certain types of objects to certain types of lists (eg add(LivingThing) overloaded by add(Bee) and add(Pollinateable), each adding to a specific list and then calling the next most specific method) it would be impossible to extend this to include classes that plugins might add without adding more overloaded methods.
The point is, all of my code should not have to take into account the existence of other types. Not all types should have to be known ahead of time, but some types need to be able to interact with other specific types that they
do know about.
If I had to add IsPollinateable methods to LivingThing, what would I have to to for every single other class that I have made, will make, or can make? It doesn't work. It can't account for classes added by external code and it forces all types to be aware of all other types
at the top-level abstract base class. It's ugly, and it's hard-coded.
If I use dynanmic_cast, that's great and all, because types can choose which other types they are aware of and the base classes can be left untouched when new types are added, but I've been told (and I haven't been told why) that this is poor class design. Yes, I've been told that checking the type of an object and downcasting is poor class design. It's pretty common in Java, though.
If I can have my code autonomously manage various lists of objects, where an object is referenced from all lists that apply to it, that would be ideal. That way, the managing code could go through all the lists, and call the appropriate Tick method from the correct context without casting and without checking the actual type of the object. This is bordering pretty close to reflection, but I'm pretty confident that RTTI is all I need.
helios wrote: |
---|
The bee wants to perform a particular action on any object of a class. The block wants to perform a particular action on a particular block. |
No, both cases are identical from a programming perspective.
TheIdeasMan wrote: |
---|
There is no sending of lists of all the objects |
That's correct. An object doesn't know and cannot know about all other objects that also exist within the same program.
@TheIdeasMan I cannot have any code in base classes that deal with or account for specific types. Specific types can inform managing classes that they exist, however.
helios wrote: |
---|
But that's one of L B's proposed solutions: to iterate over a collection of instances of a general type looking for instances of a particular type. |
That is not one of my proposed solutions. Perhaps you misinterpreted when I said that there would be multiple lists and I would iterate them all; for example, there would be a vector<LivingThing> that included all living things, a vector<Pollinateable> that included all pollinateable things, and a vector<Bee> that included all bees. There would be no need to check the type because you would know which list you were iterating, and there would be no need to cast because the list's generic type is already of the type you need.
At this point I should make it clear that there won't be one class or one part of code responsible for making everything "live".
coder777 wrote: |
---|
No, the opposite is true. dynamic_cast is perfectly safe for pointer. If you want to make virtual functions for all possible inheritances, the base class becomes a monster which will be nearly impossible to use. I had this case once, since then I take care that nothing from the inherited class infiltrate the base class |
Yes, which is my point, except I'd been told and have read that having to do that is poor design somehow.
TheIdeasMan wrote: |
---|
Of course, one could have the base classes HasPollen & NoPollen. |
Then all types must suddenly be aware of Pollinateable things whether they have anything to do with them or not, and must explicitly extend the correct base class. This is so far from possible that I'm not even going to try to think of a workaround.
kbw wrote: |
---|
In the example I gave, only those that support the necessary interface can use the pollinate function. It is a system that does not cast. So the compiler will ensure that only flowers with nectar can be pollinated. Let the type system do the work. |
You have given me a skeleton and told me that the flesh is perfectly healthy.
My potential solution:
I will have a simple abstract base class called Manager with one pure virtual member function Manage. There will be a master list of all Manager instances (all Manager classes will be singletons, the idea is to get the polymorphic behavior). The layout of Manager classes will parallel the actual classes that represent e.g. bees and flowers and Pollinateables. Every 'tick', the master list of managers is iterated and each manager has Manage called. Thanks to polymorphism, the correct context will be used to have the correct specialized behavior. How is this all set up? Upon loading a plugin, the plugin adds its manager classes to the master list of managers. Even the actual vanilla behavior of my game will be encapsulated in a plugin in order to make this work elegantly.
For those of you who have no idea what I mean or how it solves the problem, rest assured I've been writing and tsting code and it's all working flawlessly. I'll eventually get around to making a working example witht the bees and flowers. The code is currently meeting these goals:
-No use of dynamic_cast
-No use of RTTI
-No subclass-specific support in base classes
-Polymorphism and multiple virtual inheritence w/o issue or double calls
I'm very happy with my solution and plan to share it, maybe as an article or something. Thanks for all your help - readiny your arguments and discussions gave me the mental capacity to come up with my own solution that fits perfectly in all cases I can think of.
Thanks!