Chapter 3 - Dumb Stuff

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Chapter 3 - Dumb Stuff

    Here's Chapter 3.

    I replaced the SharedPtr code with Boost C++'s shared_ptr - it's much better.
    I also removed the game scripting section and moved it to the new event & scripting chapter.
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • I haven't read this yet, but I definitely think boost is buzzworthy enough to make a keyword bullet point on the back of the book.

    I'd even go so far as to say that it would attractive to boast of the use of standard APIs like STL and boost so that those who are already familiar with these APIs will be happy to pick up on a book that they can consider an extension of their use of those APIs.

    ...Sort of like how people who are into Bruce Lee will get themselves a pair of nunchucks, or better yet, people who know Adobe products tend to stick with other Adobe products because they are already familiar with the interface.

    The post was edited 1 time, last by Kain ().

  • If you design your software properly your work can be extremely efficient. With a few keystrokes you can create interesting adaptations of existing systems. Theres nothing like having such command and control over a body of code. It makes you more artist than programmer.


    I believe that art and programming are both using the same parts of the brain. Contrary to popular notion, I feel that artists are problem solvers, and they look for the best way to acieve their goals.

    Avoiding Hidden Code and Nontrivial Operations

    I feel that this section could use some mini code samples of what not to do. Also, I'd like to mention that there are good reasons to make copy constructors and assignment operators private on classes that you don't ever want to copy.

    One of my favorite mistakes game programmers make is that they either over-design or under-design their classes and class hierarchies.

    "My favorite" sounds a bit evil. I think this phrase should be replaced with something less emotional like "the most common" or something.

    On the opposite end of the spectrum a common problem found in C++ programs is the kitchen sink classthe one that has everything (including the kitchen sink).

    ...also known as the "Blob" class according to Antipatterns , by Brown, et. al. If space permits, I think it would be neat to discuss a solution to this common problem as well as any of the problems you note in this chapter. I feel that this kitchen sink class problem doesn't really belong in the section about keeping class heirarchies simple... it is more of a symptom of programmer reluctance to create new classes.

    Also on the topic of class heirachies, Lamari taught me to keep them flat as opposed to simple. This might be a better title for the section if you want to talk about why a flat heirarchy is better than tall ones with inheritance abuse, etc...

    Inheritance vs. Containment

    You give a simple example here with screens and controls, but I think there needs to be a stronger reason for this section existing than merely defining Is-A vs. Has-A... perhaps explain a type of wrong decision that people make in this category. One thing I can think of might be to illustrate how the "bridge" design pattern can be beneficial to a class. Like when you have a Sprite class that does not inherit its draw function from a base class, but instead, contains a member object that takes care of drawing for the sprite. Maybe there's a better example than this, but I think this would illustrate why you would point out that programmers should consider containment over inheritance.

    Virtual Functions Gone Bad

    This would be a good place to talk about the benefits of a flat heirarchy... then you won't have to be so careful about changing a virtual function in a base class.

    An overloaded virtual function changes the behavior of an object, and gains control over whether to invoke the original behavior. If the new object doesnt invoke the original function at all, the object is essentially different from the original. What makes this problem even worse it that everything about the object screams to programmers that it is just an extension of the original. If you have a different object, make a different object. Consider containing the original class instead of inheriting from it. Its much clearer in the code when you explicitly refer to a method attached to a contained object rather than calling a virtual function.

    Excellent description! ok, this totally invalidates my previous two comments. But now, I really think the "Inheritance vs. Containment" and "Virtual Functions Gone Bad" should be one section.

    Try to look at classes and their relationships like appliances and electrical cords. Always seek to minimize the length of the extension cords, minimize the appliances that plug into one another, and dont make a nasty tangle that you have to figure out every time you want to turn something on.

    Is this another way of saying make your heirarchy flat? These concepts you describe are quite abstract and they could really benefit from some simple examples to illustrate what not to do versus what to do.

    Consider Using Factories

    This section makes factories sound like a packaged alternative to constructors, but why not just use a constructor? I don't think the difference between factories and constructors is made clear in this section. There needs to be more emphasis on the delayed instantiation ability that factories would provide. An excellent example of this would be queuing up objects that are waiting to be constructed at the right time... such as screens. Without factories, you would have to create the screen and hold it in memory when it may not necessarily be useful at that time. With factories, you can simply push the factory object into a queue and have the manager of that queue use the factory to new up the screen after the old screen has been removed from memory.

    Implement Stream Constructors

    This topic might be the easiest to dispute so far... why make it a constructor and not just another function like "Init(InputStream&)" ? Aren't there inherent dangers from taking such a gamble on a constructor? What if the stream has bad data? This section needs more convincing because it sounds very controversial... specifically the part about it being a constructor. Why not just inherit from IStreamable and overwrite a pure virtual function like InitFromStream and WriteToStream... that way some manager can simply call some looping function on a list of IStreamable objects.

    Smart Pointers and Naked Pointers

    It might be good to point out two common pitfalls with smart pointers in this section:
    1) When two separate class objects both contain a smart reference to each other, they will never get properly destroyed and will forever be intertwince in a deadly embrace until the CPU decides to end their unrelenting hold on each other
    2) Smart pointers tend to be constructed from raw pointers unless you use that owner/carrier model. For this reason, you must be wary of ever constructing a smart pointer around a raw pointer that does not come from a new because that raw address may already be managed by another smart pointer, and for speed reasons, smart pointers tend not to check when another smart pointer has been independently created that is pointing to the same memory address as itself, and so eventually, the second smart pointer will attempt to delete an address that was already deleted.

    Reference Counting

    Begin Devil's Advocate...
    Why would you use this instead of smart pointers? How is using AddRef different from using something like boost::weakref? What does the definition of AddRef look like?
    ...End Devil's Advocate
    _____________
    No mention of boost::scoped_ptr or boost::optional?
    _________________
    The example shows how the singleton template can be used in conjunction with the scoping rules of C++ to keep track of a global variable. The global, g_FavorVRAMSurfaces, is set to true whenever the fictional texture manager should favor allocating VRAM instead of system RAM. The code inside the main function shows how each call to MakeSomeTextures() can use the default value of the global or a specific value.

    Devils advocate...
    But why did you want to temporarily use a different value for g_FavorVRAMSurfaces? Might there be a more intuitive example? The use of this scoped template seems like a bandage for some sort of abusive programming. One could argue that it would have been best to make the 'MakeSomeTextures' function take a bool parameter instead of relying on the global... in fact, the need for temporarily changing this function is a perfect example of why that variable should not have been global.
    ...end Devils Advocate.

    Also, I do not think your definition of singleton in this example matches that of the gang of four from Design patterns. Your exmaple describes more of a temporary scoped assignment or something like that. According to Design Patterns (pg 127), intent of singleton is to "ensure a class has only one instance and provide a global access point for it". This would be the kind of class that has a static Get function that constructs a class object on demand and makes sure that it is the only copy of the class. Ted Jump is not a fan of this type of usage versus a normal global because he likes full control of the objects lifetime as opposed to program scope lifetimes.

    Supporting Optional Variables with Optional<T>

    Oh, here is where you mention optional... but I felt that this belongs near the section about smart pointers as opposed to after the sections on memory. Also, boost has an optional class, although I take issue with boosts implementation of using operator! to indicate invalid... which means, !! is valid. boost::optional does provide an is_initialized() function or something like that, but they are deprecating that function for some odd reason. So this makes optional<bool> a very dangerous thing in boost. I do not think an optional class should ever overwrite operator! for this reason.
  • I was forced to make another post as that last post was at the 1000 word limit. :)
    _______________
    done. I love what this chapter is trying to accomplish, but I was very hard on the author in this chapter. The reason I chose to scrutinize this chapter so much is because the reviews on Amazon.com about the first edition of this book have shown that this particular chapter is the biggest point of contention for a lot of old style programmers out there. On one side, you have the section on using memory correctly which cannot be disputed as they are based on facts, and on the other side you have these constructs and guidelines that border on the religious to the point where holy wars have erupted in workplaces between the new generation of coders and the old generation that have been coding with raw pointers since the days of the Commodore 64... or something like that. So I would say that this chapter also needs to keep the skeptics in mind.

    Here is a quote from the Amazon reviewer I am thinking about:
    Unfortunately Mr. McShaffry fell into the same pitfall most other "here's THE right way to write code" authors fall into: he just has too many opinions and not enough facts. He touches on a lot of subjects which are programming religions, and there is no objective right or wrong for much of what he discusses. I feel some of the problem lies in the fact that McShaffry has worked on two types of games: the Ultima series and a playing card game. If you work on one codebase for years, you're going to think your solutions are perfect. As a person who has worked on numerous different types of games and engines I can tell you that there is no magic solution for writing the perfect engine. The engine is always very heavily tailored to the game desired. This is why you hear about the Quake engine, or some other third-party technology, being licensed and then gutted with major portions rewritten. So most of McShaffry's game-specific ideas need to be taken with a grain of salt. An example: smart pointers sure are safe, but if you're working on the PS2 you probably can't spare the memory or execution time for all that tracking.


    You know where I stand on this, I am a convert, but I had to be dragged into this church kicking and screaming by a very willful Lamari. I feel like I know this reviewer guy's mindset, though... and people like him tend to be very loud.

    This chapter needs to keep people like him in mind by justifying opinions more and possibly removing the points that may not withstand his scrutiny... such as the stream constructors and the temporary scoped template.

    Perhaps it can take a less religious tone and point out why some people do not want to do things the way you did it... such as not wanting to spend memory on overhead or whatnot.

    The post was edited 1 time, last by Kain ().

  • Other tidbits that may be worth discussion:

    Making a function virtual that will never be overwritten
    - You mention that you have "found that programmers create virtual functions when they dont need them", but you do not explain why the mere existence of a virtual function when it does not need to be virtual is a waste of a VTable entry.

    The use of Run Time Type Instantiation (dynamic_cast)
    - Ahhh, yes... You were the one who taught me that this keyword existed and how it is useful. I loved it. Then Lamari told me it was bad to use it... and Jeff Grills at Sony also pointed out that it was bad on a console. The pros and cons of using the dynamic_cast keyword would be an interesting discussion.
  • Wow that's a lot of good stuff - while I'm soaking all this into my brain, perhaps someone else will comment.

    Thanks tons Kain - this is exactly the kind of feedback I'm looking for.

    I wish I could spend another 40-50 pages on this stuff...
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Chapter 3 - Dumb Stuff

    • pg1. "knowing which mistakes and pitfalls to avoid..." don't I want to avoid them all? better wording perhaps?
    • pg1. "Designing good code in an object-oriented language is actually much more difficult than in a procedural language like C or PASCAL. Why? The power and flexibility of the object-oriented language allows you to create extremely complicated systems that look quite simple. This is both good and bad. In other words, its easy to get yourself into trouble without realizing it." I whole-heartedly agree, but it seems this paragraph is missing a sentence or two of explanation. Good and bad? Why? How can I get myself into trouble?
    • pg2. remove the bullet "Separate user interface code from game logic". your later comment indicates you've removed/moved it.
    • pg5. for completeness, you should insert a line in the IAnimation code the typdefs the animation list.
    • pg6. "each one returning discrete portions of the screen". I'm not sure exactly what you mean by this. Can you elaborate on this a little more, and maybe extend the factory example?
    • pg9. "//use this for reading in MT code" what is MT?
    • Anyone else having trouble with "dumb stuff"?.. what about something like Stuff Every Game Programmer Needs to Know About, or Game Progamming Essentials, etc? A lof of these concepts, shared_ptrs, memory management, etc are going to be very new and difficult for novices. Calling it the "dumb stuff" might only serve to discourage them further.
    • Overall, this chapter is good, it has good flow, and puts a lot of nice ideas into the reader's head that will help as they progress through the book.
  • Yeah, I agree with Carse on the discouraging vibe I get from the term 'Dumb Stuff'.

    This chapter is very much about coding style that could be considered "optional stuff". I happen to think everyone should code this way, but this one chapter is a very open door for those that are looking for something to wage a holy war on.

    How about something along the lines of these terms, instead?
    - Helpful Stuff
    - Recommendations
    - Coding Guidelines
    - My Laws
    - Personal rules I follow
    - Elements of my Kung Fu
    - Things you should know about me
    - Things you should know about my style
    - Things you should know about my code
    - My Style
    - The way of McShaffry
    - The McShaffry way
    - Anatomy of my brain
  • Optimization

    So, I was thinking...maybe this deserves its own chapter, maybe a section in here, maybe in debugging, maybe not.

    I think you should spend some pages on efficient code, optimiing code, and minimizing bugs in general (this is an area that you touch on pretty well). Most programming books and classes teach organization and such, but not efficiency. For instance, according to my friend (who is hardcore into optimization, which earned him a spot on a research project here...he can optimize matlab) a single comparison take about 4 clock cycles on a pentium due to the jump statement and the pipeline. This is important because, usually you'd think that regular statements and if statements are equal, when in fact (theoretically) 4 math statements take as long as a single if statement. This seems to me to be somethign worth keeping in mind when designing.

    Other things, like minimizing the work that goes inside loops, or using 32-bit integers for absolutely everything you can... this may all be worth mentioning. To be honest, I have been taught very little about efficiency, and this is definately an area that needs to be addressed.

    Avoiding common bugs is important, too, like pre-emptive error-checking (and ways to do this outside of loops), among other things. Avoiding function calls and objects where you can. All this, but with the overriding message that organization and readability really is important, and if you are going to do something crazy, use good comments (heh, explain what a good comment is).

    What do you think? Am I crazy?

    P.S. It's been a year since I've read some parts of your book, so if you mention somethign already, just ignore me (or, point me to it).
    -Larrik Jaerico

    www.LarrikJ.com
  • Sounds good to me - I sent a note to Keith at Paraglyph.

    By the way - his comments in my chapters are a lot of "Listen to Kain" and "Listen to Carse".

    You guys are really helping out!
    Thanks.
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • RE: Optimization

    Man - I'd truly love to write a whole chapter on optimization. Perhaps I can address some of this stuff in some new matieral in the debugging chapter????
    Mr.Mike
    Author, Programmer, Brewer, Patriot
  • We're Done with Chapter 3

    Marching on to chapter 4...
    Mr.Mike
    Author, Programmer, Brewer, Patriot