- Basic naming conventions
Each project should have a prefix of its own. For drawElements base libraries,
the prefix de is used. Other projects should use a different, arbitrary prefix.
For instance, the stitcher project uses the xo prefix.
Anything which has a reasonable possibility of causing a naming conflict should be
prefixed. This includes files, structs, enums, functions (except private ones), macros, etc.
In C projects, just about everything in the code needs to be prefixed (files, struct, enums,
global functions, etc.), but in C++ code, namespaces remove the need for most prefixing.
File names and macros should still be prefixed in C++ code as well. Note that members
of classes (either C or C++), or structs or unions do not need to be prefixed with the
package prefix.
Identifiers are generally typed in camelCase. This applies to file names, structs,
enums, local variables, and struct members. In some cases, prefixes are used to clarify
the behavior of a variable. Static variables are prefixed with s_, global variables
with g_, and C++ class member variables with m_. Macros and enum entries should
always be written in UPPER_CASE with underscores separating the words. Members of C classes
don't need to be prefixed.
When emulating classes in C, the class name itself should be written in CamelCase, but
starting with a upper-case letter. Usually the classes are prefixed: xoArmEmu,
deRandom, but if the class only exists within a single .c file, the prefix can be
omitted: StringBuilder. The member functions of the class should be prefixed with
the full class name and an underscore, followed by a camelCased function name:
xoArmEmu_emulateCode().
Examples of correctly named identifiers:
- dePool.c, dePool.h, deUniquePtr.hpp, deThread.cpp -- file names
- deRandom, xoStitcher -- structs / classes
- deMemPoolFlag, xoConditionCode -- enums
- DE_COMPILER_MSC -- macros
- XO_BACKEND_NEON -- enum entry
- setTableSize() -- local (static) function
- xoArmEmu_emulateCode() -- C class member function
- numVariables -- local variable
- m_itemHash -- member variable in a C++ class
- s_rcpTable -- static variable in a function
- g_debugFlag -- global variable
- Choosing good names
Naming your variables is somewhat of a black art, but the main goal of giving a name should
be clarity. You want to communicate what the contents of the variable mean. The more obscure
the purpose of a variable is, the longer (and more descriptive) a name you should invent for it.
Also, the longer the life time of a variable is, the longer a name it deserves. For example, a
loop counter which is alive for page worth of code should be named something like vertexNdx,
whereas a loop counter which lives only a couple of lines can be named simply i or ndx.
Most variables should be declared const and never changed (see coding philosophy section).
Thus one often successful approach for variable naming is to give name for the value instead.
For example when querying first child of node and storing it in variable, that should be named
as firstChild instead of node.
Consistency is one important factor in naming variables. When a similar kind of name is needed
in multiple places, choose a way of devising the name and stick to that. E.g., if you query the
number of elements in an array to a local variable in several functions, always use the same name
in each of the functions.
When dealing with counts or numbers (number of elements in an array, etc.), you should always
clearly indicate with the name that this is the case, e.g., numElements (preferred),
elementCount, etc. Which ever prefix or postfix you choose to use, stick to it.
Function parameters that have an unit of measure (e.g. seconds or bytes) should have the unit
as part of the name, for example timeLimitMs and chunkSizeKb.
Use American English instead of English English. Choose gray over grey, color over colour,
and so forth.
- Canonical abbreviations
buffer | buf |
destination | dst |
index | ndx |
source | src |
variable | var |
- Struct and enum typedeffing
For enums and structs, the types should always be typedeffed and used without the struct or
enum prefix in actual code.
Example.
/* Declaration. */
typedef enum xoConditionCode_e
{
...
} xoConditionCode;
typedef struct deMempool_s
{
...
} deMemPool;
/* Usage. */
deMemPool* memPool;
xoConditionCode condCode;
- Header files and including
All header files should have include guards in them to avoid processing them multiple times
in case they are included from multiple places. The style used for the macro is _FILENAME_H,
for example: _DEDEFS_H. Whenever including other headers from a header file, you should
always use external include guards as well. The external include guards considerably reduce the
number of file accesses that the compiler needs to make, resulting in faster compile times.
Each implementation file should have matching header file and vice versa. The implementation
file must include the corresponding header file first. By doing that, it is guaranteed that the
header file includes all of its dependencies.
Each header file should first include deDefs.h, or alternatively project-specific
xxDefs.h/hpp file that in turn includes deDefs.h. That way all the usual types and macros
are always properly defined.
External include guard example.
#ifndef _DEDEFS_H
# include "deDefs.h"
#endif
#ifndef _DEINT32_H
# include "deInt32.h"
#endif
#ifndef _DEUNIQUEPTR_HPP
# include "deUniquePtr.hpp"
#endif
The include order of files should start from debase (esp. deDefs.h), go thru
other base libraries, then your own project header files, and lastly the system header files.
Also, a .c file must include its own header file first. E.g., deMemPool.c must
first include deMemPool.h.
Every include path must also end up including deDefs.h before any actual code is processed.
This ensures that the basic portability macros (DE_OS, DE_COMPILE, etc.) have been
defined.
- Indenting and whitespace
Code should be indented with tabs (instead of spaces) and a tab-width of 4 characters should
be used.
Always put braces on their own lines. This applies to functions, structs, enums, ifs, loops,
everything. The only exception are single-line scopes. For one-statement ifs or loops, braces
should not be used. Also, put else and else if on their own lines as well.
Brace usage
void main (int argc, const char** argv)
{
if (argc > 1)
parseArgs(argv[1]);
else
{
printf("Usage:\n");
printf("...\n");
}
}
In addition to only indenting your code, things like variable names in a list of
declarations or comments at the end of line, should also be aligned such that they start at
the same column. Compare the following two examples of the same code, only with differing
alignments in the text.
Aligned variable declarations and comments.
struct deMemPool_s
{
deUint32 flags; /*!< Flags. */
deMemPool* parent; /*!< Pointer to parent (null for root pools). */
deMemPoolUtil* util; /*!< Utilities (callbacks etc.). */
int numChildren; /*!< Number of child pools. */
deMemPool* firstChild; /*!< Pointer to first child pool in linked list. */
deMemPool* prevPool; /*!< Previous pool in parent's linked list. */
deMemPool* nextPool; /*!< Next pool in parent's linked list. */
...
};
No alignments used.
struct deMemPool_s
{
deUint32 flags; /*!< Flags. */
deMemPool* parent; /*!< Pointer to parent (null for root pools). */
deMemPoolUtil* util; /*!< Utilities (callbacks etc.). */
int numChildren; /*!< Number of child pools. */
deMemPool* firstChild; /*!< Pointer to first child pool in linked list. */
deMemPool* prevPool; /*!< Previous pool in parent's linked list. */
deMemPool* nextPool; /*!< Next pool in parent's linked list. */
...
};
- Other formatting
Always use C-style comments in C code: /* This is a C comment. */ Only use
the C++ // end-of-line comments in C++ code.
Comment styles.
/* Use this kind of comments in C code. */
// This kind of comments may only be used in C++ code.
Pointer and references.
// Good: pointers and references are a part of the type
void* ptr;
deInt32* colorBuffer;
xoArmEmu* armEmu;
Array<int>& intArray;
void doBlend (deUint32* dst, const deUint32* src);
// Bad: pointer symbol should not be a part of the name
void *ptr;
void doBlend (deUint32 *dst, const deUint32 * src);
Formatting of function declarations.
// Good: void if empty param list, empty space after name, braces on own line
void doStuff (void)
{
}
// Bad: horrible function name!
void doStuff() {
}
// Good: separate arguments with spaces, function name
ShapeList getIntersectingShapes (float x, float y, float z)
{
}
// Bad: function name (list of what volumes?), no space after commas in arg list
ShapeList getShapeList (float x,float y,float z)
{
}
// Exception: sometimes simple function are best written as one-liners
float deFloatAbs (float f) { return (f < 0.0f) ? -f : f; }
Formatting of control statements.
// Good: no extra braces for one-liner if cases
if (a.isZero)
result = 0.0f;
else
result = a.value * (1.0 / 65536.0f);
// Bad: extraneous braces, bad whitespace usage
if (a.isZero)
{
result=0.0f;
}
else
{
result=a.value*(1.0 / 65536.0f);
}
// Good: expression easy to read
if (a.isZero && b.isZero)
{
...
}
// Bad: missing spaces around && operator, missing space after 'if'
if(a.isZero&&b.isZero)
{
...
}
// Good: else on its own line
if (alpha == 0)
{
...
}
else if (alpha == 255)
{
...
}
else
{
...
}
// Bad: else on same line as closing brace
if (alpha == 0)
{
...
} else if (...)
{
...
} else
{
...
}
// Good: note space after 'while'
while (numTriangles--)
{
...
}
// Bad: whitespace usage
while(numTriangles --)
{
...
}
// Good: while on same line as closing brace
do
{
...
} while (--numTriangles);
// Bad: while on its own line, missing whitespace after 'while'
do
{
...
}
while(--numTriangles);
// Good: easy to read
for (ndx = 0; ndx < numTriangles; ndx++)
// Bad: missing spaces all over (whitespace should be used to separate expressions)
for(ndx=0;ndx<numTriangles;ndx ++)
// Good: note missing braces for while, correct usage of whitespace
while (numTriangles--)
area += computeArea(triangle[ndx++]);
// Bad: don't put unnecessary braces, avoid extraneous whitespace in expressions
while (numTriangles--)
{
area+=computeArea( triangle [ndx++] );
}
Formatting switch cases.
// Good: case-statements indented, code indented another level (including breaks)
switch (blendMode)
{
case XX_BLENDMODE_NORMAL: // no variable declarations
...
break;
case XX_BLENDMODE_SRC_OVER: // need braces if declaring variables inside
{
int alpha = ...;
break;
}
case XX_BLENDMODE_XYZ:
...
// FALLTHRU! -- make non-breaked cases very explicit!
default: // handles the final blendmode (DISABLED) with an assertion!
DE_ASSERT(blendMode == XX_BLENDMODE_DISABLED);
break; // always put break!
}
// Bad:
switch(blendMode)
{
case XX_BLENDMODE_NORMAL: // always indent case labels
...
break; // put break on same level as indented code!
case XX_BLENDMODE_SRC_OVER:
{
...
break;
}
case XX_BLENDMODE_XYZ:
...
case XX_BLENDMODE_DISABLED: // always comment the case fall-through (like above)
...
} // default case missing! always need to handle it (and assert if illegal!)
Formatting of expressions.
// Good: parenthesis or whitespace used to indicate evaluation order
array[(a * b) + c];
array[a*b + c];
// Bad: order unclear
array[a*b+c];
// Good: parenthesis (or whitespace) makes evaluation order unambiguous
array[(a && b) || (c == 0)]
array[a==0 || b==0 || c==0] // in some cases spaces can be used instead of parenthesis
// Bad: unclear evaluation order
array[a&&b || c==0] // does this even work?
array[a == 0 || b == 0 || c == 0]
// Good: easy to see different parts of evaluation (whitespace where it matters)
array[triangle->index0 - cache.baseIndex];
// Bad: hard to read (whitespace around brackets doesn't help readability!)
array[ triangle->index0-cache.baseIndex ];
array [triangle -> index0 - cache.baseIndex];
// Good: easy to see all function arguments
computeArea(vtx0.x, vtx0.y, vtx1.x, vtx1.y, vtx2.x, vtx2.y);
// Bad: missing spaces makes it hard to read, no space after function name when calling
computeArea ( vtx0.x,vtx0.y,vtx1.x,vtx1.y,vtx2.x,vtx2.y );
// Good: readable (the code itself is a made-up example and thus incomprehensible)
// Consider: would probably make more readable code to use temporary variables here
if (sizeArray[a+5] > getSize(getFoo()+2))
if (sizeArray[a + 5] > getSize(getFoo() + 2))
// Bad: whitespace usage confuses rather than helps
if(sizeArray[a+5]>getSize(getFoo()+2))
if ( sizeArray [ a + 5 ] > getSize ( getFoo () + 2 ) )
// Bad: unclear (and wrong) evaluation order
if (bitMask & (1<<bit) == 0)
Other formatting.
#if defined(DE_DEBUG) // prefer #if defined() to #ifdef
...
#endif /* DE_DEBUG */ // only put ending comment if #if is far away