C++/D PROGRAMMING STYLE: · I use many underscores in my class and variable names, because I believe underscores enhance readability at the expense of maybe half a second more to type each word. This doesn't bother me one bit... those people who think that a half-second is too much time to waste while programming clearly haven't tackled making a roguelike before. · All of my variable names are descriptive, usually with at least five to fifteen characters, with multiple words as needed. Some variable names are less abbreviated if their meanings are clear. All variable names are always in lower case. In many cases, two words is preferred over one word because two words are far more descriptive. E.g. "primary_weapon", "jump_distance", "x_loc", "y_loc" · All function names and class names are also descriptive. · No variable type prefixes are included (e.g., "iByte", "fQuotient", "sString", "cClass"). This just gets in the way. You should already know the variable type of any variable you try to work with. If you don't know the type without having to check the variable name, you don't know enough about the purpose the variable serves to begin with. Just say no to Hungarian notation. · All functions that are "utility functions" -- internal functions for the program, used by other functions -- are in lowercase, with under- scores where spaces are required (which is rare). E.g., "roll100()", "output()" · All macros are in uppercase. Some inline functions which behave like macros are also in uppercase. E.g., "CONSTRAIN(0, X, 100)" E.g., "ONE_IN_(100)" · All functions that are "program functions" -- functions that the pro- gram actually uses to perform manipulation upon the data that the user would care about -- are in medial capitals. No underscores are used. E.g., "SetStamina()", "GenerateDungeon()" · Function prefixes, such as "do" or "get", are avoided if possible. All functions should start with *descriptive* verbs, not generic ones. E.g., "PickModifier()", not "getRandomModifier()". · No variables belonging to other objects are ever directly modified; all class member variables that require external access have accessor functions. All members are private unless specifically mentioned otherwise. Accessor functions are of the form "SetXYZ()" and "GetXYZ()" (this is why "get" is not used for other functions). In many cases, using accessor functions to modify data is discouraged in comparison to using other utility functions (such as using "Move()" to relocate the character, rather than SetX(), SetY(), and SetZ()). · Constant identifiers (i.e., for functions which do not cause any changes to the variables they access) are used far less often than OOP says they should be. This is because I would not change a varia- ble unless I actually wanted to. I guarantee that. · Reference variables are preferable to pointers, but all instances must remember the pointer to themselves (the "this" pointer is not enough), and must have a pointer to their pointer on the object array. These pointers must be publicly accessible. · All of the tab stops in my code are set to *TWO* spaces, because I believe small indentation sizes enhance the amount of indentation you can do and discourage inconsistent indentation. All indentation is done with tabs, and all blocks are always indented by precisely one tab. (You have no idea how much it irritated me when I looked at the inconsistent indentation of the Nethack source code. Tabs and spaces? Ugh.) · All comments are done in the C++-style "//" whenever possible, even if they are multi-line. Multi-line comments are only done in C-style "/*" and "*/" if they are excessively large. · C-style comments are not graphically enhanced with asterisks or any other junk: /* This is a plain, no-nonsense multi-line comment. Note that I have not indented the text, nor have I dressed it up like a working girl by inserting pointless asterisks. */ · The exception to this is in the file header. All files have a header in the following format. /************************************************************** [PROJECT NAME] - [FILENAME] - [File Synopsis] *************************************************************** File description goes here. 4/22/2005 9:00 AM - Modification history goes here. - jtg **************************************************************/ · Different sections of the code are separated with C++-style comments in the following format: ///// SECTION ///// No real rules exist for separating sections; separation can be wher- ever I feel like it. · As a general rule, if a file is more than 500 lines long, it should be broken up into two more-specifically-named files. Exceptions to this rule aren't usually frowned upon. · All comment symbols are flush with their text (i.e., there are no spaces between the symbols and the first letter of the comment). If a C++-style comment spans more than one line, subsequent lines are "indented" with a space between the symbol and the text. For example: //This function acts to create a furrow in the space-time // continuum, thereby eliminating the hapless player and // wreaking ruination upon the kingdoms of men. · Comments are usually complete sentences, unless a sentence fragment will adequately explain the goal being attempted. Periods are op- tional, unless more than one sentence is in a single comment. · Comments are always of the nature of explaining why code is doing what it is, not explaining what the code is doing. I.e., "Determine the distance to the target so we can pick a proper weapon", and not merely "Calculate distance" (and definitely not "Set c to the square root of a + b"). · All operators (excluding parentheses, which don't really count) are separated from the variables and literals they manipulate with spaces. E.g., "2 + 3", not "2+3". This obligation is waived in cases where it improves readability to compress the operators; e.g., (2*(roll100()+32)) · All text in the code is wrapped at 80 characters, with the carriage returns escaped with backslashes to make it clear that the line does not end abruptly. The subsequent lines are indented by one level. · Text that is wrapped in this fashion is always broken up immediately after an operator, with a space between the operator and the back- slash. For example: pointless_math = (x + (2*y))^0xFF07 + sqrt(32/speed) + \ (32*z) / 125 · D Note: The backslash-carriage return is not part of the language lexicon. I think this is damned stupid, since an operator with no apparent rval is *not* sufficient to indicate a continuation on the next line (it could just as easily mean the line wasn't finished at all). A special symbol is required to make the continuation evident. Thus, whenever I'm programming in D, I use the ~ concatenation opera- tor to signify a continued sentence, but unfortunately can't do any such thing for "dangling" operators. In no case will I ever refuse to wrap the code at 80 characters. · All if/while/etc. statements are flush against the parenthesis of the condition. This is because you would be a fool to try to make up your own if() or while() function, given that they're reserved words, and they're clearly distinguishable from functions because program func- tions always commence in upper case. To wit: if(condition) else if(condition) while(condition) for(initialisation; condition; statement) · All variables are declared on their own line (with rare exceptions in the interest of saving space). All variables are always initialised, even if only to 0 or NULL. · The asterisk to declare a variable as a pointer is associated with the variable type, not with the variable name. All variables are declared on their own line, so there is no need to attach it to the variable name. uint32* integer_pointer = &other_integer; · All braces start on the same line the block is opened, and end indented with the block: while(alive) { //begin while if(blah) { //begin if DoSomething(); DoSomethingElse(); } //end if else if(blee) { //begin elseif DoSomethingElseEntirely(); } //end elseif } //end while This is done to encourage proper indentation; you'll note that the indentation is what's important here, not the braces. If you're used to looking at braces to see different blocks, you can forget about it right now. Whitespace-syntaxed languages are superior. · The same, however, is not true for functions and classes, whose braces appear in the more widely-adopted style: void function() { FunctionContents(); } This is done to make the beginning and end of functions easier to distinguish. · Whitespace is free of charge, so it is used liberally. · Classes should be separated by three carriage returns from one another. Functions should be separated by two carriage returns from one another. · All functions that are larger than one screen (about 50 lines) in length have braces commented, unless their structure is not particu- larly complex. · Most functions should be less than one screen in length, per normal. · I don't stick "void" in the parameters of a function if it accepts no arguments. I think that's just stupid, and whoever recommended that at ANSI needs to be smacked across the face with it. · Never use assignment within an if() statement. If the development environment or compiler has support for it, then warnings of this sort should be enabled. · All warnings are errors. Think of this in terms of real life: "Sir, if you speed, you're breaking the law. We may catch you speeding, and if we do, you're going to get fined." "Okay, officer!" [speeds off and gets pulled over by the same officer] Sure, he might've gotten away with it if the cop wasn't looking when he sped off. But why did he risk it? · In comparisons, variables go on the left side, constants go on the right side. Using the compiler to catch errors is admirable, but the consistency is thrown off. //SAMPLE FUNCTION (IN D PROGRAMMING LANGUAGE): //I've replaced tabs with spaces to make sure it comes out properly in your // web browser. //Incidentally, I wrote this function before I discovered the function that // already existed within the compiler's standard libraries. ;-) /* Converts a string to uppercase. PRE: 'str' is a D character array of any length, 'special' is true or false POST: returns string in uppercase (with accented characters also in uppercase if the 'special' variable is set to true); 'str' variable is unaltered. I don't like using a "magic number", but ASCII/UTF-8 was deliberately designed with 0x20 between lowercase and uppercase, in all cases except ÿ (0xFF) and Ÿ (0x9F). */ char[] uppertext(char[] str, bool special=false) { char[] returnstr = str[0 .. (str.length)-1]; //copy string for(uint32 i = 0; i < str.length; i++) { if(str[i] >= 'a' && str[i] <= 'z') returnstr[i] = str[i] - 0x20; else if(special) { if(str[i] >= 0xE0 && str[i] <= 0xF6) returnstr[i] = str[i] - 0x20; else if(str[i] >= 0xF8 && str[i] <= 0xFE) returnstr[i] = str[i] - 0x20; else if(str[i] == 0xFF) returnstr[i] = 0x9F; } } return returnstr; }