Code Style Standard

Comments

  • All code shall be commented sufficiently that its operation and motivation would be clear to a reader who is familiar with the language and with Scout but has not read other project code or otherwise communicated with the coder
  • Comments should not provide information that would be obvious to a competent engineer reading the associated code
    • However, different engineers may have different values of obvious (or different values of competent :), so use your discretion
    • Bad:
      // Loop until there is no more data to read in
      while (read(file) != EOF)
    • Good:
      while (read(file) != EOF)
  • Comments should provide any information otherwise absent that would be necessary or helpful for a competent engineer to understand the code
  • Comments should document major design decisions made while writing the code
  • If you fix a particularly subtle bug, a comment contradicting the false assumption that caused the bug might be appropriate
    • Use your discretion
  • Comments should minimally describe the purpose of a particular piece of code and its inputs and outputs
  • Publicly (outside the file) visible unctions should be preceded by a Doxygen (/**) block comment describing purpose, usage, parameters, side effects, and return value
    • Here is a Doxygen tutorial to get you started
  • This may be appropriate for other globally visible objects as well
    • TODO: which ones?

File Naming

  • Filenames shall effectively hint as to file content and have a dot-delimited extension that reflects file type
    • Bad: New File 5
    • Good: motors.c
  • Filenames shall be composed entirely of lower-case English letters, digits, periods, and underscores

Function Naming

  • Function names should be meaningful and unambiguous
  • Use under_scores (no camelCase)
    • i.e. usb_puts, motors_init
  • First letter should be lowercase
  • First "word" should describe the actor, following words describe the action
    • i.e. use motors_init instead of init_motors
    • multi-word examples: bom_get_max, range_read_distance

Variable Naming

  • Variable names shall be meaningful and unambiguous
    • e.g. a, var1, and number are probably bad variable names; motor_direction is an OK variable name
    • Seemingly bad variable names are allowed where significant precedent makes their meaning clear
      • e.g. i and subsequent one-letter variables for loop indexes
  • Two variables in the same scope shall not vary only in capitalization or the use of underscores
  • If two variables in different scopes have identical purposes, it may speed reader comprehension if they have the same name
    • e.g. a function parameter and the argument passed to the function in that position

Macro Naming

  • Names should be meaningful and unambiguous
  • All caps with underscores between words

Use of Whitespace

  • Braces beginning and ending blocks go on their own line, with the same indentation as the enclosing block
  • Each nested block of code shall be indented from its containing block by 4 spaces (actual space characters)
    • No tab characters shall appear in the source code
  • No lines exceeding 80 printing characters in length
    • Long lines that must be broken to comply with this rule shall be broken in such a way that each individual sub-line means as much by itself as possible
      • Bad:
        uint32_t function_name(uint32_t parameter1, uint32_t parameter2, uint32_t
                too_many_parameters);
        Reader must remember parameter type across line break
      • Better:
        uint32_t function_name(uint32_t parameter1, uint32_t parameter2,
                uint32_t parameter3);
        Reader need only remember that they are reading the parameters to a function
      • In other words, break between tokens with the least nested syntactic relationship practical
    • When breaking long lines, use a hanging indent of two normal indents (8 spaces)
      • Bad:
        if (/* overly long condition expression requiring
            a line break */)
            do_this();
        Second line lines up with opening parenthesis, but is it part of the condition or the body?
      • Better:
        if (/* overly long condition expression requiring
                a line break */)
            do_this();
        Second line is clearly part of condition expression, not if body
  • Statements with parenthesized operands like if, while, and for shall have exactly one space between the statement and the opening parenthesis
  • Conversely, function-call-like statements (function calls, function-like macro usage, compiler operators like sizeof()) shall have no space between the function name and the opening parenthesis
    • This dichotomy helps visually differentiate constructs whose syntax is similar but whose semantics are not
  • Lines shall not have extra whitespace after the last non-whitespace character (excluding the terminating newline character, obviously)
  • Most binary operators should have a space between themselves and their operands
    • Exceptions are when the combination of the two operands by the operator is more naturally thought about than the sequence of the three tokens
    • Bad:
      a=b+c/d+e.f->g
    • A little better:
      a = b + c / d + e . f -> g
    • A lot better:
      a = b + c / d + e.f->g
  • There shall be no space between a parenthesis and the statement it encloses
    • Obnoxious:
      if ( function( a, b, c ) == d )
    • Less so:
      if (function(a, b, c) == d)
  • There shall be one space between opening comment delimiters (// or /*) and the beginning of the comment
  • The indentation of comments shall match that of the surrounding code
    • That is, comments shall be indented as if they were lines of code
    • See examples for finer details of readable comments
    • Bad:
      //This is a comment
      /*So is this
      multi-line comment*/
    • Better:
      // This is a comment
      /* So is this
         multi-line comment
       */
  • Two consecutive empty lines should never appear
  • Files should end with a single newline

Parentheses and Other Grouping Operators

  • Avoid unnecessary use of parentheses, as mentally determining grouping frustrates reader comprehension
    • If you can't remember the order of operations, look it up. You will remember it better later, and your code will be better and cleaner for it.
    • Exceptions in some cases where historical precedent or regularity suggest the use of parentheses, like sizeof(variable)

Data Types and Macro Usage

  • Use C99 data types as opposed to traditional data types to increase clarity and prevent arithmetic bugs
  • Use size_t for array indexes
    • TODO: Appropriate use of the other special types dealing with pointers and the like is also desirable, but I confess that I cannot remember what they all are or what they are for
  • Global variables accessed only from within their containing C file should be declared static
  • Variables not intended to be written to should be declared const
  • Compile-type constants should be specified using object-like macros (#define)
    • If you find yourself defining several constants that go together, use an enumerated type instead, e.g.
      #define SUN 0
      #define MON 1
      #define TUE 2
      #define WED 3
      #define THU 4
      #define FRI 5
      #define SAT 6
      becomes
      typedef enum
      {
          Sunday = 0,
          Monday = 1,
          Tuesday = 2,
          Wednesday = 3,
          Thursday = 4,
          Friday = 5,
          Saturday = 6
      } weekdays;
  • Function-like macros (especially complicated or multi-step ones) should be avoided unless a worthy goal cannot be accomplished without them
    • Use functions declared inline instead, or better yet, tell the compiler to automatically inline small functions and don't worry about it
  • http://www.roboticsclub.org/redmine/wiki/colony/AVR_data_types is instructive