Project Coding Conventions ***** Declarations, Naming and Hungarian The following Hungarian conventions are used in the tree, as a minimum. Other conventions may be added to these, but should not change the meaning of any of these conventions. If a variable does not start with Hungarian, it should begin with a capital letter (Foo) to so indicate. Otherwise, the first letter after the Hungarian should be capitalized (pfnFoo). These prefixes are ordered in the order of their precedence (So m_ is before p is before fn, so you write m_pfnFoo). Prefix Meaning Why ------------------------------------------------------------------------------- g_ Global Prefix of global variables. m_ Member of Class All methods of class will have these variables always in scope. They need to be different to protect the namespace in methods. p Pointer Always helpful to know if variable is pointer or pointer pointer (pp) h Handle Denotes a reference to memory that cannot be directly interpreted as a pointer b Bool To distinguish TRUE/FALSE containing variable from other integers c Count (signed integer) To indicate that this integer contains a count of something. Is signed so (--count < 0) tests can be used. f Floating point (single) To indicate native floating point type. d Floating point (double) fn Function Usually with pfn to indicate a pointer to a function. i Signed integer i and u are used to distinguish signed and unsigned integers o Offset Used to flag memory offsets. Useful in memory structures in DSP and Chip simulations. s String s and sz distinguish between non-zero terminated and zero terminated strings. sz Zero Terminated String u Unsigned integer 'p' should be used in conjunction with a base type tag for pointers to simple types, so a pointer to an unsigned integer is 'pu' rather than just 'p'. Complex types do not have tags so pointers to structures or objects will only have a 'p' prefix. ***** Differences From Other Hungarian Styles Many kinds of Hungarian are used around the company, ranging from very strict to very relaxed. The Hungarian described here is fairly relaxed and the following Hungarian conventions are not used: There is no distinction between far and near pointers. Pointers are always 'p'; there is no 'lp'. Arrays are not distinguished with a special prefix. int iFoo[7] instead of int aiFoo[7] or int rgiFoo[7]. Functions are not tagged for return type. int Foo() instead of int iFoo(). Variables holding flags are not specially distinguished. UINT32 uFlags instead of UINT32 grfFlags or UINT32 fFlags. Classes and structs do not have tags. FooClass *pFoo instead of FooClass *pfcFoo. Examples: TCHAR g_szDllName[80]; PHANDLE phFile; PD3DVERTEX pVtx; C++ const's follow the Hungarian of their type. #define's are all cap's. Classes are mixed case starting with a capital. Structures are all cap's, with "tagNAME" used to name the structure, and "NAME" used to name the typedef: typedef struct tagCHUNKELEM { struct tagCHUNKELEM *pNext; EscNodeBase *pNode; EscMatrix *pMatrix; } CHUNKELEM, *PCHUNKELEM; Structure types should always have a P typedef associated with them and P should be used in favor of *. ***** Standard Types A set of standard types of known size has been defined. These types should be used for any data element that must be a particular size. typedef signed char INT8; typedef short int INT16; typedef long int INT32; typedef __int64 INT64; typedef unsigned char UINT8; typedef unsigned short int UINT16; typedef unsigned long int UINT32; typedef unsigned __int64 UINT64; typedef float FLOAT; typedef double DOUBLE; If a data element does not require a particular size then the standard INT and UINT types can be used. FLOAT and DOUBLE are IEEE standard formats and take 32 and 64 bits, respectively. The standard Win32 TCHAR should be used for strings where the character set can be ANSI or Unicode. __TEXT() can be used to declare TCHAR strings and the CRT file tchar.h has many standard routines #defined for Unicode and ANSI builds. ***** Comments for Files, Functions and Code Comments should use // instead of /* */ unless an in-line comment is absolutely necessary. All files must have a descriptive header comment which gives information on the contents of the file and any other file-specific information. C/C++ style: //----------------------------------------------------------------------------- // // This file contains code dealing with foo and also bar. // // Copyright (C) Microsoft Corporation, 1997. // //----------------------------------------------------------------------------- Assembly style is the same except with ; instead of //. ;------------------------------------------------------------------------------ ; ; This file has optimized routines for foo. ; ; Copyright (C) Microsoft Corporation, 1997. ; ;------------------------------------------------------------------------------ All functions and methods must have a header comment giving an overview of what the function does, what its inputs are and what its outputs are. C/C++ style: //----------------------------------------------------------------------------- // // CreateFoo // // Takes a bar and a baz and creates a foo from them. If the foo is // successfully created it is returned and the baz is updated to // reflect the new count of foos using it. // //----------------------------------------------------------------------------- Assembly style has ; instead of // and more information about exactly how the code is entered and left. If the function is declared with arguments then no explicit documentation is necessary in the header, but if arguments are passed in registers then the header should indicate this. Return values are assumed to be in eax unless otherwise specified. ;------------------------------------------------------------------------------ ; ; CreateFoo ; ; Takes a bar and a baz and creates a foo from them. If the foo is ; successfully created it is returned and the baz is updated to ; reflect the new count of foos using it. ; ; Expects bar in eax and baz in ebx. ; Destroys ecx and edx. ; ;------------------------------------------------------------------------------ All code should have at least a simple comment for each basic block and more comments as necessary. Assembly code should be heavily commented with notes on register and FP stack usage, unusual tricks, instruction pairing issues, etc. Classes should generally have a function-like comment describing the reason for their existence and an overview of their interface. Trivial classes do not need a comment. Structs generally need only a mention of their use, although if they have complicated features or methods they may need a class-like header. ***** Microsoft Internal Any code or comments which are for internal use only are bracketed as follows. The brackets and text are stripped for the file versions which are made public. //@@BEGIN_MSINTERNAL microsoft-only stuff here //@@END_MSINTERNAL ***** File Naming We'll be using a mix of C and C++ so we need to distinguish files that are C-compatible from those that are not. Use .c for plain C files. Use .h for plain C headers or headers which can be included safely in plain C files. This includes headers which have C++ constructs protected by ifdef __cplusplus. .h files that may be included in C++ code should have internal extern "C" blocks. Use .cpp for C++ files. Use .hpp for C++-only headers. ***** General Code Style Lines should not exceed eighty columns. This is much more pleasant for those of us that do not use large windows. Functions should be fewer than one hundred lines long unless structure or performance requires a longer function. In general functions should be even shorter, with sub-functions created as necessary. Any commented-out code must have an explanatory comment with it describing why it is commented out but not removed entirely. Global variables should be avoided unless absolutely necessary. Use uSize members of structs that may need to be resized later with backwards compatibility. When using uSize make sure to validate sizes everywhere to force callers to use it properly. Headers should be protected with #ifndef #define #endif blocks for inclusion safety. ***** C++ Usage Guidelines C++ has a lot of things in it which can make code more difficult to read or slower. The following is a list of suggestions on things to avoid. These rules can be broken if necessary. Avoid overloading operators. Avoid default parameters. Avoid multiple inheritance. Avoid exceptions. This means that constructors cannot fail since they can't return anything. If an object requires initialization that can fail the constructor should put the object in an invalid state and a method called Initialize should be used to fully create the object. Avoid public data members. Use accessor methods instead. Avoid placing method bodies inline in class declarations. Bodies of inline methods should follow the class declaration in the header file. Avoid naming conflicts with functions so that :: is not needed. ***** C/C++ Code Style Indentation and spacing should be done entirely with spaces; no tabs should be used. One indentation level is four spaces. Braces should be on separate lines and aligned with each other. All statements must be bracketed even if they are only a single line. Case statements should align with the brackets of the switch. break statements in case statements should be indented once from the case. Functions should be declared so that their name begins on the first character of a line. Return types, calling conventions and other decorations should be on a preceeding line. Arguments can immediately follow the name. Functions should always declare a return type and use (void) if they have no arguments. Complete Example: foo.hpp: //----------------------------------------------------------------------------- // // This file contains the declaration of the Foo class. // // Copyright (C) Microsoft Corporation, 1997. // //----------------------------------------------------------------------------- #ifndef _FOO_HPP_ #define _FOO_HPP_ // Flags for the Foo class. #define FOO_INVALID 0x00000001 #define FOO_MATCH 0x00000002 // Special numbers for checking. #define FOO_RETURN_NEGONE 0xfefefefe #define FOO_DO_NOTHING 0xefefefef //----------------------------------------------------------------------------- // // class Foo // // The Foo class is responsible for keeping a buffer of things to check. // //----------------------------------------------------------------------------- class Foo { public: Foo(void); BOOL Initialize(int cElts); ~Foo(void); int Check(int iCheck); inline UINT32 GetFlags(void); private: INT m_cElts; INT m_cUsed; PINT m_piBuffer; UINT32 m_uFlags; }; //----------------------------------------------------------------------------- // // Foo::GetFlags // // Accessor method for a Foo's flags. // //----------------------------------------------------------------------------- inline UINT32 GetFlags(void) { return m_uFlags; } #endif // _FOO_HPP_ foo.cpp: //----------------------------------------------------------------------------- // // This file contains the implementation of the Foo class. // // Copyright (C) Microsoft Corporation, 1997. // //----------------------------------------------------------------------------- #include "foo.hpp" //----------------------------------------------------------------------------- // // Foo::Foo // // Initializes a Foo to an invalid state. // //----------------------------------------------------------------------------- Foo::Foo(void) { m_piBuffer = NULL; m_uFlags = FOO_INVALID; m_cElts = 0; m_cUsed = 0; } //----------------------------------------------------------------------------- // // Foo::Initialize // // Constructs a Foo. // //----------------------------------------------------------------------------- BOOL Foo::Initialize(int cElts) { // Allocate a buffer. m_piBuffer = new int[cElts]; if (m_piBuffer == NULL) { return FALSE; } m_cElts = cElts; m_uFlags = 0; return TRUE; } //----------------------------------------------------------------------------- // // Foo::~Foo // // Cleans up a Foo. // //----------------------------------------------------------------------------- Foo::~Foo(void) { delete m_piBuffer; } //----------------------------------------------------------------------------- // // Foo::Check // // Checks for the given number in the buffer. If a match is found, // return the index of the match. If no match is found, add the number // to the buffer and return its index. If the buffer overflows, -1 is // returned. // // If the number is one of the special numbers, return its special code. // //----------------------------------------------------------------------------- int Foo::Check(int iCheck) { int i; // Check for initialization. if (m_uFlags & FOO_INVALID) { return -1; } // Check for a special number. switch(iCheck) { case FOO_RETURN_NEGONE: return -1; case FOO_DO_NOTHING: break; default: break; } // Look for an existing match. for (i = 0; i < m_cUsed; i++) { if (m_piBuffer[i] == iCheck) { m_uFlags |= FOO_MATCH; return i; } } // No match was found, so add the new number if there's space. if (m_cUsed == m_cElts) { return -1; } m_piBuffer[m_cUsed] = iCheck; return m_cUsed++; } ***** Assembly Style All assembly ops and registers should be in lower case. Each line is indented eight spaces from the left margin. Assembler directives and macros are always in uppercase. Complete Example: ;------------------------------------------------------------------------------ ; ; This file has optimized routines for foo. ; ; Copyright (C) Microsoft Corporation, 1997. ; ;------------------------------------------------------------------------------ .386p .MODEL FLAT .CODE ;------------------------------------------------------------------------------ ; ; Foo ; ; Takes a pointer to a bar and returns the total bit count for it and its ; successor. ; ;------------------------------------------------------------------------------ Foo PROC PUBLIC, pBar:PBAR LOCAL cBits:DWORD ; Get bit count for current bar. mov ecx, pBar mov eax, [ecx+BAR_cBits] mov cBits, eax ; Get successor bar in ecx. push ecx call UpdateBar mov ecx, eax ; Sum bit counts for return. mov eax, cBits add eax, [ecx+BAR_cBits] ret Foo ENDP END ***** Asserts, Debug Output and Other Debugging Aids Assert checks should be placed where appropriate. These should be used where unexpected but still possible bad things can happen. Note that parameter validation will occur in the D3D runtime, so checking of state and items like vertex type is not necessary. We will use the new DX DPF support defined in newdpf.h.