view doc/devel/style.txt @ 21:1c9dac05d040

Add lint-style FALLTHROUGH annotations to fallthrough cases. (in the parse engine and thus the output code) Document this, because the old output causes warnings with gcc10.
author David A. Holland
date Mon, 13 Jun 2022 00:04:38 -0400
parents 12171da8943f
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))