TinyMUX Coding Guidelines

From TinyMUX
Jump to navigation Jump to search

Introduction

No coding style is perfect in purpose or perfectly internally consistent. Also, many style choices are necessarily arbitrary -- that is, there may be no compelling benefit for picking one element of style over another even if there is a benefit for consistently applying a style.

Names

Avoid short, cryptic names. Longer names are generally better than shorter ones. Names should be spelled correctly. Names should allude to their higher-level semantics of the problem domain instead of their lower-level language type.

Hungarian

The Hungarian naming notation was developed by Charles Simonyi at a time before object-orientation and powerful symbol-aware editing tools were widely available. Historically, there are two main variants: Systems and Apps. Both variants have been widely used in Microsoft code, and its use continues if now somewhat discouraged if for no other reason than new code must use existing APIs.

In the Systems variant, the prefix encodes the lowest-level type (dw for DWORD, l for LONG, p for pointer). In the Apps Variant, the prefix encodes the highest-level type or most-abstract purpose (rw for row, str for string).

Simonyi's original vision was for each project from project inception to choose unique prefixes for all common project data types. These prefixes were expected to be useful only within the project or only within a family of projects within the same application domain. The Apps variant is arguably closer to Simonyi's vision, but published platform APIs tend to show the Systems variant.

TinyMUX is only minimally influenced by the above, and use of Hungarian notation within TinyMUX is not encouraged.

Declarations

At a minimum, place each declaration of different type on a separate line. It is preferable to declare each variable on a separate line, but the following is permitted:

char *foo;
char  bar, baz;

Tabs

No hardtabs. Spaces only. No spaces left at the end of the line.

Indention

Four spaces per indention level.

TinyMUX uses the Allman style of bracing. The tools are there to easily find matching braces, but is still visually helpful if matching parenthesis, square brackets, and curly braces occur either in the same row or the same column.

if (x == y)
{
    p++;
}

The curly braces are at the same indention level as if control keyword (if, while, switch). The contained block representing a new scope is indented one level. For structures and unions,

typedef struct NODE
{
    int foo;
} NODE;

NODE Nodes[2] =
{
    1, 2
};

For switch, the case keywords should be at the same indention level as the switch keyword. Cases can be combined without additional comment or extra blank lines, but after each block of statements, either use a break keyword or an explicit 'fall through' comment. Use a default case, and put the default case either first or last in the list of cases. A blank line should separate each case.

switch (ch)
{
case '-':
    y = ch;
    // fall through

case 'a':
case 'A';
    x = ch;
    break;

default:
    y = ch;
    break;
}

While not required, deeply-nested Boolean expressions can be expressed in the following style:

if (  a == b
   && (  c < d
      || d < e + 1
      || p == q))
{
    ...
}

The && and || operators appear under their corresponding opening parenthesis. Notice this breaks spacing, indention, and matching rules described elsewhere. However, one can easily check that && and || always alternate (otherwise, unnecessary parenthesis have been used), and each clause is lined up and appears in the order it will be evaluated. This form also makes it easier to perform DeMorgan transforms with the ! operator.

For less complex boolean expressions, this degree of styling is not necessary.

Spacing

Use a single space after each comma between function arguments. Do not use spaces between the parenthesis and function arguments. Do not use spaces between the function name and the leading parenthesis. Do no use spaces inside square brackets. Do use a space after control flow keywords. Do use a space before and after comparison operators.

while (x == Foo(a, b, ch))
{
    ch = str[x];
}

Comments

Doxygenated comment blocks should be expressed with /* */. Otherwise, either syntax (// or /* */) may be used for comments.

Comments should not simply restate the plain meaning of source code in psuedo-English. They need to reveal information not easily revealed from a plain reading the code itself. Comments should use proper grammer and punctuation.

Also, while either syntax (// or /* */) can be used for comments, there is still the question of how many lines of code should be covered by a comment: one line or several adjacent ones. The former, we will call line-oriented; the latter, block-oriented.

Line-oriented comments appear in the legacy portions near some global variable definitions, but generally, it is very difficult to reveal anything high-level about a single line of code that isn't already obvious.

Instead, a block-oriented style is preferred.

    // We look for the lost puppy in all of the locations we know about.  Finding
    // the puppy this way is relatively expensive.
    //
    bool bFound = false;
    for (int i = 0; i < N; i++)
    {
        if (IsPuppyHere(i))
        {
            bFound = true;
            break;
        }
    }

NULL

When using NULL to indicate whether or not a variable has been initialized, explicitly use NULL in the comparison. Do not leave pointers uninitialized or left to point at random memory:

if (NULL != p)
{
    FooRemoveBlock(p);
    p = NULL;
}

Subset the Language

To avoid being one typo away from assigning a constant to a variable instead of comparing against it, use constants as lvalues where possible:

if (NULL == p)
{
    ...
}

Don't use the greater-than and greater-than-or-equal (> and >=) comparisons:

if (  minimum <= x
   && x <= maximum)
{
   ...
}

Don't use pre-increment and pre-decrement. Avoid using post-increment and post-decrement except by themselves:

p++;
q--;

Just because a language allows something does not mean that it is good practice to use it.

Magic Numbers

In general, use named constants using #define, const, or enums instead of unnamed literals.

#define FOLDER_NAME_LEN 10
char szFolderName [FOLDER_NAME+1];
if (FOLDER_NAME_LEN < strlen(pNewFolderName))
{
    ...
}

const size_t nFolderNameLen = 10;
char szFolderName[nFoldernameLen+1];
if (nFolderNameLen < strlen(pNewFolderName))
{
    ...
}

The notable exception to this rule (and others) is strcmp() and friends:

if (strcmp("b", "a") < 0)
{
    // "b" is less than "c".
    //
    ...
}

if (strcmp("b", "b") == 0)
{
    // "b" is equal to "c".
    //
    ...
}

if (strcmp("b", "c") >= 0)
{
    // "b" is greater than or equal to "c".
    //
    ...
}