view doc/devel/style.txt @ 17:12171da8943f

Don't refer to CVS.
author David A. Holland
date Tue, 31 May 2022 01:56:37 -0400
parents 13d2b8934445
children
line wrap: on
line source

Style guidelines for AnaGram source
-----------------------------------

Purpose:

   These guidelines are *guidelines*, not commandments. Legibility and
   maintainability are more important than mindless adherence to
   regulations.

   Note: patches that reindent or reformat will be rejected. Patches
   whose only purpose is to reindent or reformat will be rejected
   extra-fast. If you think a section of code needs reformatting,
   write a message saying so and explain your reasoning. Illegibility
   and violation of the principle of least surprise are good reasons.
   Lack of strict conformance to these guidelines is not.

   Note: some source files have DOS-style end of line (CR/LF) and
   others have Unix-style end of line (just LF) - for the most part
   this should be transparent. So leave things the way they are in
   this regard.

Basic layout guidelines, in order of decreasing generality:

   - 2-space indent.
   - Lines do not exceed 79 columns.
   - Don't indent preprocessor directives.
   - In a multi-line /* */ comment, the beginning and ending tokens
     get their own lines, and all the stars should line up.
   - Left braces go at the end of the line.
   - Right braces get their own line.
   - 'else' starts a new line.
   - Use braces with if, else, while, do/while, and for.
   - If violating the previous rule, always put the subordinate
     statement on the same line.
   - Violate said rule for conciseness only when:
	- a single if (no else) has a very short conditional and a
	  very short subordinate statement;
	- a while statement ditto;
	- or a for statement has no body.
   - Never violate said rule for a do/while statement.
   - Put the while part of a do/while statement on the same line as
     the closing brace, to keep them from getting separated.
   - Switch case labels should be indented two spaces, and code
     indented two spaces further.
   - Each switch case label should get its own line.
   - Switch code should not be on the same line as a switch case.
   - Condensed switches (one line total per case) should only be used
     if all or nearly all the switch can be formatted thus and some
     benefit to legibility accrues.
   - Infinite loops should be written as "while (1)", not "for (;;)".

Please do not:

   - Misgroup tokens when declaring pointers. "char* s" is wrong. It
     should be "char *s".
   - Write your comparisons backwards. "if (0 == x)" is really
     unnatural to read.
   - Leave off the space after while, for, and if, or, conversely,
     insert a space between a function name and its arguments. while,
     for, and if are not functions.
   - Use gratuitous parentheses with return to make it look like a
     function call.


Include files:

   - Include files should be grouped in this order:
	- standard C includes
	- OS, compiler, or GUI-specific includes
	- AG's own includes

   - Within these groupings, headers should appear in alphabetical
     order. (Why...? Why not?)

   - Exceptions: any <sys/*.h> should come before other standard
     headers. And if any <sys/*.h> are used, <sys/types.h> should come
     first. Also, "pf.h" should be included after any standard headers.

   - Further exceptions: AG's log header should come last, and should
     be preceded by a commented-out "#define INCLUDE_LOGGING".
     Examples abound.

   - To the extent practical, don't use (even within AG_ON_UNIX)
     header files that aren't standard or standardish Unix, or that
     would require build-time tests in the configure script to make
     work right.

   - Each AG header file should be idempotent, that is, a source file
     that just includes it should be compilable and its compilation
     should not be affected by whether other headers have been
     included or in what order.

   - Each header should have an #ifndef guard in the standard form to
     protect against repeated inclusion.

   - Do not use the standard <assert.h> within AG - only use AG's own
     "assert.h".


ifdefs:

   - Use #ifdef AG_ON_UNIX and #ifdef AG_ON_WINDOWS for
     platform-specific code. Don't use per-compiler ifdefs for this;
     in the long run compilers are more portable than you think.

   - Don't use #ifndef AG_ON_UNIX or #ifndef AG_ON_WINDOWS, unless
     it's to issue a #error.

   - Don't use #if defined() in place of #ifdef.

   - Do mark #endifs with the symbol from their matching #ifdef,
     #ifndef, or #if, unless that directive is only a few lines above
     and easily recognizable.

   - Use compiler-specific ifdefs only where you have to. If possible,
     abstract the construct out to minimize the amount of conditional
     code.

   - If a compiler doesn't have a well-known, safe, and/or easily
     recognizable symbol to #ifdef on, adjust the makefiles to set
     one when that compiler is used, rather than litter the code with
     possibly flaky ifdefs.

   - Don't use processor-specific ifdefs. Write portable code.


C++ language restrictions:

   - Do not use STL headers.

   - Do not use << >> operators for I/O.

   - Declare functions with no arguments as explicitly taking void,
     like you would in C.

   - Don't do wild things with templates.

   - Don't do "for (int i=0; ...)" - we have a compiler that uses the
     really old scoping semantics for this.

   - Use your common sense. Neither the language nor compiler has any.


Portability concerns:

   - When using fopen, always use "b" with binary files. Don't use "t"
     for text files, as this is nonstandard and at least one Windows
     compiler's library objects to it.

   - When using open(), always use either O_BINARY or O_TEXT.

   - The proper type associated with strlen() and sizeof() is size_t.
     Don't use "int" or "unsigned". The signed return value of Unix
     read() and write() has type "ssize_t". Cast that back to size_t
     after checking it for being an error return.

   - When using printf, don't use the C99 %-specifier for size_t;
     always cast size_t to unsigned long and print with %lu.

   - Assume that time_t may be 64 bits wide, even on 32-bit platforms.
     If you need to print one literally, use %lld and cast to long
     long.

   - Don't assume that all pointers are the same size, or that
     pointers are the same size as either int or long.

   - In the mainline AG code avoid using anything that might be
     locale-dependent, as the user interface might have set some crazy
     locale.

   - Don't blindly slice filename strings using path separator
     characters. Use appropriate functions instead. And avoid slicing
     filename strings unless necessary.

   - Always pull the name component out of a pathname before looking
     for any filename suffix. Otherwise you lose on names like
     "foo.d/bar".


Robustness concerns:

   - If you mean to fall through the bottom of a switch case (one that
     has code) put /* FALLTHROUGH */ there.

   - If you have virtual functions in a class, declare the destructor
     virtual too.

   - If you have functions in a class that are virtual because they're
     declared that way by a base (ancestor) class, declare them
     explicitly virtual yourself too.

   - Don't define virtual functions inline, unless there's no other
     practical place to put the definition.

   - Use parentheses when mixing && and ||, or &, |, and ^, or in
     other cases where operator precedence is easily confused.

   - If you need an assignment inside a conditional, wrap the
     assignment in parentheses and an explicit comparison. (Not just
     extra parentheses.) But don't do this unless there's a reason for
     it.

   - If using qsort() from <stdlib.h>, be sure your compare function
     only returns 0 for identical objects. If the objects aren't
     identical, always pick *something* to distinguish by, so the sort
     is deterministic.


Idioms:

   - YES:  if (!strcmp(a, b))
     NO:   if (strcmp(a, b) == 0)

   - YES:  if (strcmp(a, b) != 0)
     NO:   if (strcmp(a, b))