The Nightstar Zoo

Nightstar IRC Network - irc.nightstar.net
It is currently Sun Dec 17, 2017 8:10 pm

All times are UTC - 6 hours [ DST ]




Post new topic Reply to topic  [ 11 posts ] 

Are forward declarations a kludge?
Kludgy as a global variable. 0%  0%  [ 0 ]
Mostly harmless 50%  50%  [ 2 ]
Good programming practice 50%  50%  [ 2 ]
Total votes : 4
Author Message
 Post subject: fdecl a kludge?
PostPosted: Mon Jun 28, 2004 4:33 pm 
I was recently reading on IRC about a programming problem a friend was having where an instance of class A was a member of class B, but class A had to have a pointer to B in it. The question came up of the circular dependency, and the fact that each class had to be declared before the other one. Now, where I come from, the solution is simple and straightforward. You forward-declare class B, include A first, and happily compile your code.

The wrinkle was that, apparantly, class B was about forty different classes, and when the fdecl solution was suggested, his response was "I would have to forward declare 40 classes, and that is too kludgy, so I want another solution."

Now, the way I was taught, way back in the day, you fdecl everything in your code in order to reduce compile dependencies. It would be even nicer if you didn't have to fdecl anything, but with the inherently single-pass C/C++ compiler, it's not an option. So, when I create a project, I generally end up putting tens (or perhaps hundreds?) of fdecls, one for each class and global function in a project, into one header (fdecl.h) and then include it everywhere as a common header.

I can see a potential maintenance issue if you're adding or changing classes often, but that's usually a design-time consideration, and after the initial design, (hopefully) won't happen very often during routine maintenance. Even then, good tools and documentation should mitigate the problem.

Am I being curmudgeony again? Is there some kind of new elegant OO paradigm they're teaching in programming schools these days that says you should never fdecl your classes, because there might be a lot of them and that would be a kludge?


Top
  
 
 Post subject: Re: fdecl a kludge?
PostPosted: Mon Jun 28, 2004 4:53 pm 
Heck, I think that the argument against fdeclaring your functions predates the wide use of OO techniques. I first heard about it from Nick Wirth's languages. Argued as a way to prevent infinite loops in recursion. I unconsiously avoid fdecls if I can in my DynC code. My code ends up looking more like Pascal programs, with the lowest level code at the top and the main function at the very bottom...

I think most C libraries follow your design though, The header file contains piles of fdecls, and everything references them, including the library itself.

I believe that Wirth's more recent languages caved to the forward declaration demands, as shown in http://www.oberon.ethz.ch/oreport.html in section 10. This indicates to me at least grudging acceptance of the need for forward declarations in the real world. I don't believe Modula allowed forward declarations.

There's some things that are darn hard to do without forward declarations though. I personally think that you should only put them in when needed, but that's just my own bias as a turbo pascal-trained programmer speaking.

Speaking of my Pascal trained nature, I also dislike how C does headers and modules. Darn annoying, that is.


Top
  
 
 Post subject: Re: fdecl a kludge?
PostPosted: Mon Jun 28, 2004 5:21 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Pi wrote:
The wrinkle was that, apparantly, class B was about forty different classes, and when the fdecl solution was suggested, his response was "I would have to forward declare 40 classes, and that is too kludgy, so I want another solution."

  1. Having a child member that points to it's parent is a bad design. I would tell your friend that this is already too kludgy and that forward decls are one way of kludging your way out of the bag.
  2. I have never, ever seen a professional design where a class needed to know the details of more than two or three other classes. There's that smell coming from the design again.
  3. You should forward declare exactly as many classes as you have to, and no more. If you're really married to the design (have I mentioned that it smells?) then fdecl where you must.

Pi wrote:
Am I being curmudgeony again?


Of course. But you're probably also right.

Pi wrote:
Is there some kind of new elegant OO paradigm they're teaching in programming schools these days that says you should never fdecl your classes, because there might be a lot of them and that would be a kludge?


Well, I didn't want to say anything before now, but I do think there's probably something wrong with the design.

Having said that... let me think. I used to design everything with full duplex interfaces like this. I believe it's a common problem that programmers coming from a procedural or imperative mindset have: I want everything, right now; when A wants something from B it should get it right now and vice versa. Oh, now I have a design loop. Hmm. The solution for me was to spend a few years learning how to decouple these interfaces.

Multithreading and message queues are common solutions that you come up with to avoid having these guys talk directly to each other. A more simple design simply would say class A is a member of class B and keeps it's damn hands to itself. Class B will poll it frequently to pass alerts and messages up the chain, but A is not to call back into B.

If you absolutely must use this (Godawful) design, consider this instead:

Class A knows how to talk to a class_A_client.
Each instance of Class B inherits from class_A_client, and has class A as a member. It creates an object of A and tells it "I am here", passing it its class_A_client interface.

But do take some time to sit down and think through the "I am a __________. I know how to __________ myself" exercise. Class A seems to know too much about class B. Perhaps an inversion of priority is in order?

Now, this is a dodge of the main question: forward declarations. I think they're lovely. They're a fantastic way to enforce module-level scoping. I voted them "mostly harmless", though, because I don't forward declare unless I explicitly need to expose something outside a module or need to forward declare a class/struct pointer.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 29, 2004 1:50 am 
Offline
Energizer Bunny
User avatar

Joined: Wed May 22, 2002 12:24 am
Posts: 1634
I've only really had occasion to fdecl a class exactly once:

I had Vector3D, and then I had Matrix3D, and they talked about each other a lot - they were in fact very closely coupled. I couldn't, however, find a decent way to decouple them - Matrix3D had several constructors that needed to have Vector3D arguments, and Vector3D had several methods that needed Matrix3D arguments. In order to get them both to compile, I needed to fdecl Matrix3D. (also, since they were so closely coupled, I put them both into one file.)

Vorn


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 29, 2004 2:06 am 
Offline
Energizer Bunny
User avatar

Joined: Wed May 22, 2002 12:24 am
Posts: 1634
I guess what Chalain is really trying to say is that fdecls are not so much a kludge as a Code Smell - something that says "You have probably designed this wrong."

Vorn


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jun 29, 2004 9:26 pm 
When I learned Pascal in high school, I was taught to forward-declare all of my functions, and I kept that habit in C/C++.


Top
  
 
 Post subject:
PostPosted: Tue Jun 29, 2004 9:42 pm 
Offline
Energizer Bunny
User avatar

Joined: Wed May 22, 2002 12:24 am
Posts: 1634
Nmm. You always forward declare functions. You rarely if ever forward declare classes.

Vorn


Top
 Profile  
 
 Post subject: Re: fdecl a kludge?
PostPosted: Wed Jun 30, 2004 3:49 am 
I admit it, I am said friend. :)

Chalain wrote:
  • Having a child member that points to it's parent is a bad design. I would tell your friend that this is already too kludgy and that forward decls are one way of kludging your way out of the bag.
  • I have never, ever seen a professional design where a class needed to know the details of more than two or three other classes. There's that smell coming from the design again.
  • You should forward declare exactly as many classes as you have to, and no more. If you're really married to the design (have I mentioned that it smells?) then fdecl where you must.

Now, let me start by saying that there is only one place where this coupling happens: A particular function in each game object class updates physics for that class for the latest frame.

For background, every game object is viewed the same way. They update, they draw, whatever. Enemies might update AI, while players update input. In short, the parent class is just a way of saying "You're All the Same". It's pure virtual, so it's not a seperate class in its own right.

Possibilities (using the Player object as an example of which one is updating currently):
  • Pass that update function a list of objects it might collide with, and have it check collision itself against each, including collision resolution should one of those checks hit.
  • Pass in one object at a time, and call the Player's update collision function once per object that it may collide with. This means that there's an external function which essentially acts as a proxy, but each object still knows (and handles) how it reacts to specific types of collisions (You can have Player->enemy, enemy->background, player->platform, etc, but as far as anything outside the game object classes is concerned, they're all the same object).
  • Do ALL collision code outside the object.
Now, the last option there is right out. I've tried it before and it devolved into a mass of kludge. I also had misgivings about the first option (my original design).

Since talking [edit]Vorn, I have already changed it from the first to the second. All I've really done is seperate the collision detection from the rest of the update mechanism.

Quote:
Class A knows how to talk to a class_A_client.
Each instance of Class B inherits from class_A_client, and has class A as a member. It creates an object of A and tells it "I am here", passing it its class_A_client interface.

Curing the disease by killing the patient? :P

Quote:
But do take some time to sit down and think through the "I am a __________. I know how to __________ myself" exercise. Class A seems to know too much about class B. Perhaps an inversion of priority is in order?

And here we have it. All the various game objects interact with is each other, and I already said they all pretty much behave the exact same way (since they're all versions of the same basic type). The only point of question is this: Am I interacting with one object at a time, or am I interacting with a list? It was the latter, now it's the former.

Quote:
I don't forward declare unless I explicitly need to expose something outside a module or need to forward declare a class/struct pointer.

Exactly my opinion.


Last edited by Raif on Wed Jun 30, 2004 2:07 pm, edited 1 time in total.

Top
  
 
 Post subject:
PostPosted: Wed Jun 30, 2004 11:33 am 
gwalla wrote:
When I learned Pascal in high school, I was taught to forward-declare all of my functions, and I kept that habit in C/C++.


Heh, your teacher was wierd, then. I was taught that you avoid fdecl if possible. The only place where you universally fdeclare your functions is inside a Unit, in turbo pascal. All functions you want to be public need to be forward declared in the interface section.


Top
  
 
 Post subject: Re: fdecl a kludge?
PostPosted: Wed Jun 30, 2004 2:21 pm 
Offline
Nightstar Graveyard Daemon
User avatar

Joined: Mon Jun 03, 2002 8:30 pm
Posts: 1071
Location: Wouldn't you rather observe my Velocity?
Raif wrote:
I admit it, I am said friend.


Oh, then I take back all the nice things I said. :P

Raif wrote:
Now, let me start by saying that there is only one place where this coupling happens: A particular function in each game object class updates physics for that class for the latest frame.


I guess I don't see how or why they would end up mutually coupled. I've written this kind of code, it ended up looking like this:
Code:
// detect collisions and apply input forces but do not actually move or update the objects
for(i=0; i<objects.count(); ++i) {
    objects[i].process_collisions();
}

// do any custom processing here--fixup objects that have
// collided with terrain or eachother, crash bikes that have been
// hit too hard, etc.  These fixups consider the physics matrices as
// input in terms of what the game *wants* to do with each object;
// if intervention is required be sure to clear out any unwanted impulses
// or accelerations or velocities.
...
// done

// move objects, update physics matrices & clear out impulse matrices
for(i=0; i<objects.count(); ++i) {
    objects[i].update();
}


All of this shows that the framework knows who its objects are and how to manipulate them, but the objects only know about themselves. The fixup code was pretty kludgy and had the ability to consider two (or more) objects at one time but the objects themselves did not interact one with another.

Not saying this design was better; just that this was how we did it. :-)

Raif wrote:
Do ALL collision code outside the object. ... [this] is right out.


I wouldn't be so hasty. Objects do not know about physics, or at least they shouldn't. They defintely got no business telling other objects how to move and react. The design problem you're running into is that you want each object to know how to collide with every other type of object out there. Remember that smell I mentioned? It's coming from here.

I've recently (past 72 hours) worked through a similar problem with an RPG design. If you have 5 hit points, and I shoot you for 5 points of damage, do I get to tell you you're dead? If I roll dice to attack you and you roll dice to defend, which one of us gets to declare that my swing was a hit or a miss? Answer: neither. The Referee does. The Ref understands all the fiddly nuances of combat, like your armor is +2 but I'm a ranger and you're my racial enemy so I get +4 to hit. The Referee understands that my 5 points of damage is fire-based, and you are resistant to fire or extra susceptible or whathaveyou. Here's what I came up with:

I am a character. I know how to update my hit points. I know how to calculate my attack modifier. I know how to find my weapons, and they know what type of damage they do and how much dice. I know how to roll damage with a weapon.

I am a Referee. I know the rules of the game. I know how to find the characters and monsters in the game. I know how to conduct and resolve a round of combat by telling the characters to roll to attack and roll damage dice. I tell characters when they've taken damage and how much, and I tell them if they're stunned or dead.


My point is, an object shouldn't know the rules (physics) of the game. A key example is whether or not characters die if their hitpoints reach 0. Do they? Maybe, maybe not. It's not up to the character to decide. It's up to the referee. "No, you die at -10, at 0 you are unconscious, and at -1 you lose 1 HP per round until you are given first aid which returns you to 0HP or you reach -10 and die."

An object should know how to __________ itself once physics are done to it. If I tell the object "You are experiencing a 50n force in this direction, accelerating at 12.23m/s^2 in that direction, and moving at 5.06m/s in this other direction," it should be able to figure out how to integrate that into a new accel, velocity and position. It should be able to tell another object "You're colliding with me, and my elasticity is X". But it shouldn't be able to tell another object what it's impulses are.or how to react to it. And here's the kicker: it also shouldn't be able to investigate the properties of another object and determine how it reacts. That's the referee's job. Otherwise, every object must know about all the fiddly wierdages of the rules you have set up, like "this object weighs 5kg, but has a mass of 5000kg even though the current gravity is 1g"

Physics is just rules. Leave them up to the ref. :-)

I am an object. I know how to behave once I've been told how to react. I know what space I take up, and whether or not another space collides with me. I know what forces I would apply to a point given a collision. I know how to move myself and, because I am poorly designed, I also know how to display myself through OpenGL instead of leaving this up to a display class. But I digress. I am an object, and this is my lot in life.

I am a physics engine/referee. I know what rules are currently in effect. I know how to process a timeslice by checking objects for collisions, getting their collision forces, and factoring in the rules. I tell the objects how to react.


I understand the desire to think that objects should understand physics, because physics are universal and constant and blah blah blah. I have played D&D without a referee because all of us were fair and honest. But here's the catch: every one of us was also also completely versed in all of the rules for every character class and monster type in the game. In short, each of us had to be a complete referee for it to work. You might be tempted to think that because the laws of physics are well-defined and unchanging, you can get away with this in your objects. But I suspect that you can't. Here's why:
  • The map is not the terrain, the painting of the pipe is not the pipe... and the implementation of the physics model running on the finite state machine is not the laws of physics.
  • You are only modeling a subset of the laws of physics. Think your laws don't change? Hah. As soon as you've got rigid-body collisions working, some idiot on the team will ask you to add point-spring collisions. Now you have new object types to add to the engine, and new rules to add to the referee. Did you want to add that to The referee, or to every other object in the game that is masquerading as a referee?
  • You are writing a game, not a simulation. Though some games do remain faithful through and through, they are the minority. As soon as you have the physics working you're going to get asked to make the obligatory ice level, and a moon level, and a hyperelastic superbounce level where the elasticity of terrain but not some objects is increased, etc. In short, your rules are probably going to change. Even if your game doesn't permit changing the laws of physics, if you intend to reuse your framework, you've just placed a design restriction on every game that reuses it.
  • The DRY principle compels you. Don't Repeat Yourself. The rules belong in exactly one place, not in each object in the system.


I suspect you've already got a grasp on that last rule, because I just realized that if you wanted to follow the DRY principle but not have a referee, you'd have... a superclass which understands how to interact with all of its children--the Original Problem Statement. Good work. Now start over. :-)


Top
 Profile  
 
 Post subject: Re: fdecl a kludge?
PostPosted: Wed Jun 30, 2004 3:15 pm 
Chalain wrote:
Oh, then I take back all the nice things I said. :P

Bastard. ;)

Quote:
My point is, an object shouldn't know the rules (physics) of the game. A key example is whether or not characters die if their hitpoints reach 0. Do they? Maybe, maybe not. It's not up to the character to decide. It's up to the referee. "No, you die at -10, at 0 you are unconscious, and at -1 you lose 1 HP per round until you are given first aid which returns you to 0HP or you reach -10 and die."

You have a point. I think I've discovered an Evil Hybrid of your version and mine, whereby physics objects still know how to collide with each other (circles, boxes, other primitives if time permits), but the resolutions of those collisions are external to the object (as per your idea).

Quote:
An object should know how to __________ itself once physics are done to it.

Lines like this STILL make me giggle.

Quote:
If I tell the object "You are experiencing a 50n force in this direction, accelerating at 12.23m/s^2 in that direction, and moving at 5.06m/s in this other direction," it should be able to figure out how to integrate that into a new accel, velocity and position. It should be able to tell another object "You're colliding with me, and my elasticity is X". But it shouldn't be able to tell another object what it's impulses are.or how to react to it.

I take it back, my system is turning out to be EXACTLY like yours. I'm going to go ahead and interperet this parallel thinking as a positive sign, even though I know you. ;) *dodges thrown object*

Quote:
I know how to move myself and, because I am poorly designed, I also know how to display myself through OpenGL instead of leaving this up to a display class.

I'm going to argue that point. :) I have 9 (I think) layer objects, each of which will probably render a collection of a specific type of sprite. There's one for enemies, one for particle effects, one for the background, etc. So far this model is working out EXTREMELY well. For instance: The background is made up of a great many tiles that often share the same texture. It never moves or rotates. Therefore that render code is written with a static object in mind (semi-static: parallax backgrounds move around slightly).

Now, keep in mind I wouldn't do it this way for a full 3d engine, but this is obviously a 2d engine implemented in 3d. :P

Quote:
As soon as you've got rigid-body collisions working, some idiot on the team will ask you to add point-spring collisions.

Nope. This is a 6 month project, and fortunately I have no idiots on my team. :)

Quote:
You are writing a game, not a simulation. Though some games do remain faithful through and through, they are the minority. As soon as you have the physics working you're going to get asked to make the obligatory ice level, and a moon level

Interestingly enough, this would be really easy with the way we've implemented the physics object (as distinct from how the game objects use physics). All it would take is a variable gravity (where now it's constant), and a slight change in how we handle acceleration that opposes the velocity vector.

However, this won't be happening either. :)

Quote:
In short, your rules are probably going to change.

This I'll grant, which is why I brought up the dependancy question in the first place. I'm designing the framework with modifiability as a key prerequisite.

Keep in mind our collision dection, movement, and resolution code have all been seperated from the start. :)

Quote:
he DRY principle compels you. Don't Repeat Yourself. The rules belong in exactly one place, not in each object in the system.

Gah! Acronyms! The echoes of YAGNI still reverberate through my poor skull. ;)

Quote:
I suspect you've already got a grasp on that last rule, because I just realized that if you wanted to follow the DRY principle but not have a referee, you'd have... a superclass which understands how to interact with all of its children--the Original Problem Statement. Good work. Now start over. :-)

Yes indeed. You are an evil man. But MY kinda evil.


Top
  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 6 hours [ DST ]


Who is online

Users browsing this forum: Google [Bot] and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group