Bare in mind that these guidelines were written years ago just to convey my style and try to keep it uniform (which didn't work
), so they might be dated.
@SupSuper:
I have some suggestions if you'd like to consider them:
Why not considering to extract a method with a meaningful name in such situation? Such a method is easier to test and easier to debug, and extracting methods allows later on to see if "doing this is stil a responsibility of this class, or maybe we need a new one?" Also inlining code is more optimal only in certain situations, overall it is ok for performance to make a function call, because when bloating code, you put a lot of stuff into the processors 1st line cache, hence having less place in the 1L cache for the game's data, and increasing the cache misses rate, which in turn are very bad for performance.
Sure, I didn't mean to put comments over refactoring, since I also wrote this:
Avoid copy-pasting, avoid incredibly long code blocks, use spacing and comments and even more functions to keep everything neat.
What I meant by that is sometimes you just have code that's just
hairy and no amount of refactoring will make it look good (like really mathy stuff, edge cases, etc.) and it's good to have a comment to clarify that stuff. Programmers have a sixth-sense when code just looks "off" and it's good to clarify when it's intentional.
In general this is how I go about comments:
- By default code should be self-documenting. So clear names for functions, variables, etc.
- Use the one-line brief comment (in the header) to sum up what the function is for. This is normally unnecessary but it also shows up on Auto-Complete which looks cool.
- Use the descriptive comment (in the source) to sum up
how the function does it, for people diving into it.
- As a last resort, use a regular in-line comment to explain any particularly weird code line.
I disagree with the first one, and the reasoning I have nullifies the second one. I propose to make it: "Never put multiple declarations. Each declaration has it's own line".
It is easier to read:
int a;
int* b;
int c;
int *d;
than:
int a,*b,c,*d;
The reasoning behind int* a; being better than int *a; is that int* gives a stronger intuition of a type - pointers are also distinct types after all, and you can also make references to pointers like int*& a; (which in turn are types as well) or pointers to pointers as well.
Another thing is padding. Making many fields make's you lose count easily - if you have five bools in a row, and then an int then you expose your classes for problems with padding memory -> objects take more memory than it actually is required -> you get more cache taken, but not usable -> performance goes down.
Ehhhh... I see your point, and I don't think there's ever a case where pointers/references are mixed all in one line (because that's just ugly).
But I hate having variable declarations all in separate lines. All in one line is ugly too, I think they are are two extremes. I prefer a good mix. Like if I have a bunch of related or similar vars, I would do "int x, y, width, height;". The grouping implies they make sense close together. I dunno it just seems neater to me, I wouldn't wanna find an "int x" at the top and then track down the "int y" at the bottom.
Assembly code generated for ++i and --i for an int/long/char has equal complexity ( both generate 3 assembler instructions ) - this is valid only if the operator pre/post incrementation/decrementation is defined for big objects, but it is discouraged. Overloading of the ++ or -- for types other than BigInts etc is a bad practice and destroys readibility of the code.
It's mainly for consistency with stuff like STL iterators where ++i and --i is slightly faster than i++ and i--.
I personally think i++ and i-- looks better though, and this whole pre/post is a whole bunch of nonsense and there should be just one type problem solved.
In general it is a good practice to make generic stuff. If you make a private method, then it is worth considering if this functionality could be use for other classes.
If the answer is 'yes' it's good extract that method to another class as a static method (perhaps templated) and reuse the code. Less code == less places you need to maintain, test and track bugs in.
I don't mean to knock generic
code, just generic
classes, like putting it all in a big Utility class or whatever.
What I'd add is also to try to use polimorphic classes, to reduce the hordes of if/else statements and switches. The generated assembler code for a polimorphic method call is just a single assembler instruction more, which makes it more efficient than using if/else/switch because there is also no forking and branch prediction incorporated.
We don't avoid polymorphic classes out of performance worries, believe me.
I'd say it's just convenience? Game concepts rarely fit nicely into "polymorphic objects", since there can be a lot of confusing and crazy variations of special behavior and so on. It's easier to work with flat value-based classes in that regard, even if it makes for if-else-nests, and they're easier to externalize for modding.
I'm not saying this is
good, but it works. In retrospect a component-based architecture could've been more robust, but it could've also scared away everyone that doesn't have a clue what I'm on about.
Software architecture is tricky, specially with an open-source project.
Btw, were you guys doing code reviews before commiting the changes to the repository ? it's worth doing, believe me.
We usually review any pull requests that come from contributors before merging them into the codebase. But within the lead team (SupSuper, Daiky, Warboy), we have very disparate schedules, tasks and lives (which is why pull requests are often left hanging, sorry
), so we just trust each other to code properly and discuss if we have any problems or questions when we get together.