forked from Alepha/Alepha
404 lines
13 KiB
Plaintext
404 lines
13 KiB
Plaintext
Basic Code Formatting Rules:
|
|
|
|
0) Like most things in Alepha, these rules are "strong guidelines". When in doubt, these rules
|
|
form a baseline of good current practice. However, there's no substitute for good judgement.
|
|
Therefore, a large degree of formatting latitude is afforded to code. The primary value
|
|
being sought is clarity, not consistency. Therefore, new stylistic forms are constantly being
|
|
adopted for specific cases -- it is cumbersome (at best) to keep track of all of them here.
|
|
|
|
Reason: Format should convey meaning. It's fine to invent new conventions. Documenting them is
|
|
good too, but it shouldn't be cumbersome to do so -- thus this file itself will always be an
|
|
incomplete representation of the format.
|
|
|
|
Exceptions: Certain core guidelines are universally required, like tabs.
|
|
|
|
1) Alepha code uses tab character ('\t', ASCII:0x09) for indentation. One tab per indent level. Tabs
|
|
are not to be used for alignment, except with other tabs. Tabs have no expected set width.
|
|
|
|
Reason: Tabs permit customization of code indent level to a reader's taste via the tabwidth settings
|
|
of editors.
|
|
|
|
Exceptions: None. The tab character as _indent only_ permits cooperation amongst those with different
|
|
visual preferences.
|
|
|
|
2) Semicolons on all lines shall appear immediately after the last syntactic character, without a separating space,
|
|
immediately followed by a newline (or trailing comment). It is permissible put a semicolon on its own line to
|
|
facilitate a block structure where appropriate for the language.
|
|
|
|
Examples:
|
|
|
|
++i;
|
|
f( a, b );
|
|
|
|
const bool value= true
|
|
or a
|
|
or b
|
|
;
|
|
|
|
3) Declare only one variable per "line".
|
|
|
|
Examples:
|
|
|
|
int x;
|
|
float *y;
|
|
int &xr= x;
|
|
|
|
Reason: C++ declaration grammar inherits ambiguities and oddities from C. By declaring one variable per line, we sidestep this issue.
|
|
|
|
Exceptions: In some cases it may be clearer to declare multiple variables of a non pointer and non reference type on a single line:
|
|
|
|
Examples:
|
|
|
|
int x, y, z;
|
|
std::string s1, s2;
|
|
|
|
|
|
4) All bracing constructs, "(), {}, [], and <>", must have a space between the opening brace and the first
|
|
argument, and between the last argument and the closing brace. If there are no arguments then there should
|
|
be no spaces between the opening and closing brace. No space shall exist between a keyword or an entity name
|
|
and the following opening bracing construct. A newline character counts as a space for these constructs,
|
|
thus permitting block-like structure.
|
|
|
|
Examples:
|
|
|
|
if( someFunction( argument ) ) return 1; // Good. Notice that there is no trailing space after the "if"
|
|
std::vector< std::map< std::string, std::set< std::string > > > complexDataEntity; // Even for templates
|
|
complexDataEntity[ 10 ][ "Hello "].find( "Something" ); // Even for array-like operations.
|
|
std::cout << [](){ return "Hello World!"; }() << std::endl; // For one-lining lambdas and initializers, the rule applies.
|
|
for( int i= 0; i < 10; ++i ) std::cout << i << std::endl; // Even within a loop construct.
|
|
|
|
Reason: Extra space can be visually helpful. Many styles encourage visual cues to code flow.
|
|
|
|
Exceptions: Spacing should not be used in #include statements.
|
|
|
|
Examples:
|
|
|
|
#include <iostream>
|
|
|
|
|
|
5) All 2-argument operators that do not modify the left hand side (binary-function operators) must have a space
|
|
on both sides of the operator, whereas data-modifying equals-based operators like ' =, +=, -=, *=, /=, ^=, |=,
|
|
&=, ~=, <<=, >>=, %= ' shall have no space to their left, thus binding them to the expression that they modify.
|
|
Note that ' !=, <=, >= ' are not assignment-like operators, but a comparison operators. Comma "operators"
|
|
shall also follow left-hand no spacing rules, as this matches their natural use in other circumstances.
|
|
|
|
Examples:
|
|
|
|
int a= 10;
|
|
int b= 11, c= 12;
|
|
c= ( a * b + c ) / ( a * 22 );
|
|
a%= c / b;
|
|
if( ( a != b ) or ( c < a ) && ( a= b ) ) return true;
|
|
|
|
Reason: Extra visual space helps. Cuddling the assignment operators helps distinguish them from comparison
|
|
operators. "<=" vs "<<=" and "==" vs "=". By having extra code-intent presented in a side-channel (spacing),
|
|
it helps prevent accidental typos and bugs.
|
|
|
|
Exceptions: `<=` when used as a "left fat arrow" operator may be used with the spacing rules that most
|
|
make sense for their context. `<=[]` on lambdas, for example. `/` when used as a directory separator
|
|
between path components from `std::filesystem` should also omit surrounding spaces.
|
|
|
|
|
|
6) All unary operators shall "cuddle" their applied variable. Pointers and references shall be be declared
|
|
with the "&" or "*" attached to the variable name.
|
|
|
|
Examples:
|
|
|
|
int a= 10;
|
|
++a;
|
|
a--;
|
|
a= ~a;
|
|
if( !a ) return false;
|
|
|
|
Reason: Somewhat improves readability. For declarations, although "int &" is the type of "int &x;", this
|
|
does not extend to: "int &x, y". Those are distinct types, but by banning multiple declarations per line,
|
|
we mostly avoid this issue.
|
|
|
|
|
|
7) Brace placement shall be "Allman/BSD"-like. Opening braces shall be on a new line, closing braces on a
|
|
new line. Braces shall be indented by the same level as the line of their "control" statement. A braced-block
|
|
that is not subordinate to a control statement shall be indented as if it had an "if( true )" control statement
|
|
above it. In addition, for such a non subordinate block, it is strongly recocmmended to have an introducing
|
|
comment that helps set the block apart. do-while loop shall have the while on its own line, after the closing
|
|
brace, where possible. Note that one-line compaction is permitted, as is omitting braces for fully subsumed blocks.
|
|
|
|
Examples:
|
|
|
|
int
|
|
main()
|
|
{
|
|
int x= 10;
|
|
int y= 11;
|
|
if( x < y )
|
|
{
|
|
std::vector< int > v;
|
|
if( y % 2 )
|
|
{
|
|
rv.push_back( v );
|
|
}
|
|
|
|
// Note that this if block has no parent, but it is indented like it has one.
|
|
{
|
|
std::list< int > l( v.begin(), v.end() );
|
|
if( x == y ) for( const auto &x: stuff )
|
|
{
|
|
std::cout << x << std::endl;
|
|
}
|
|
std::cout << l.size() << std::endl;;
|
|
}
|
|
|
|
do
|
|
{
|
|
std::cout << v.size() << std::endl;
|
|
}
|
|
while( false );
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Exceptions: Very small functions or blocks can be subsumed into one line, for brevity.
|
|
|
|
8) Return types shall be placed above the name of the function in definitions, but on the same line for
|
|
declarations. inline or explicit for ctors, dtors, and conversion functions shall follow this rule as well.
|
|
|
|
Example:
|
|
|
|
bool is_even( int x );
|
|
|
|
bool
|
|
is_even( int x )
|
|
{
|
|
return ~x & 1;
|
|
}
|
|
|
|
|
|
9) Template clauses for definitions shall be on their own line. For declarations, a separate line is optional.
|
|
|
|
Examples:
|
|
|
|
template< typename T > bool is_even( T x );
|
|
|
|
template< typename T >
|
|
bool
|
|
is_even( T x )
|
|
{
|
|
return ( x % 2 ) == 0;
|
|
}
|
|
|
|
|
|
10) In class definitions, public, protected, and private regions shall be indented beyond the access control
|
|
keywords, which must also be indented. In switch/case blocks, cases shall be similarly single-indented,
|
|
as too breaks shall remain single indented. All switch statements shall have a break, return, or throw
|
|
at the end of each case clause or shall use [[fallthrough]] to document fallthrough behavior, where
|
|
it is used. Default clauses are not mandatory, but are recommended. (Default clauses might be blank,
|
|
or all cases might be covered, especially when the argument is an enum.)
|
|
|
|
Examples:
|
|
|
|
class Foobar
|
|
{
|
|
private:
|
|
int x;
|
|
|
|
public:
|
|
void setX( int newx ) { x= newx; }
|
|
int getX() const { return x }
|
|
|
|
void
|
|
operation( int val )
|
|
{
|
|
switch( val )
|
|
{
|
|
case 2:
|
|
x= 11;
|
|
break;
|
|
|
|
case 11:
|
|
x= 2;
|
|
break;
|
|
|
|
case 12:
|
|
specialCase();
|
|
[[fallthrough]];
|
|
|
|
default:
|
|
x= val;
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct Open
|
|
{
|
|
int x;
|
|
};
|
|
|
|
Exception: Indenting break one more than case is acceptable, both as a preference and because an auto formatter
|
|
may not distinguish between a switch/case/break and a for/break or while/break.
|
|
|
|
|
|
11) Continued lines shall be indented twice past the main parent's indent. When breaking a line on an operator,
|
|
the next line should begin with an operator. Spaces may also be used to line up nicely for vertical alignment
|
|
of operators; however, note that spacing alignment starts from the tab-depth of the previous line. (Never use
|
|
spaces to align to a specific tab depth.) Control structures which have a single subordinate statement
|
|
on a separate line shall enclose that line in braces. If wrapping becomes necessary with "one line" control
|
|
structures, then bracing is required.
|
|
|
|
Example:
|
|
|
|
int
|
|
main()
|
|
{
|
|
std::cout << "A really long message which "
|
|
<< "needed to be indented "
|
|
<< "across multiple lines." << std::endl;
|
|
|
|
std::cout << "A really long message which "
|
|
<< "needed to be indented "
|
|
<< "across multiple lines. "
|
|
<< "This message was aligned "
|
|
<< "on ostream operators."
|
|
<< std::endl;
|
|
|
|
if( condition ) action();
|
|
|
|
if( moreComplicatedCondition )
|
|
{
|
|
moreComplicatedAction();
|
|
}
|
|
}
|
|
|
|
Reasons: It is important to know when lines have wrapped. A specific visually distinct
|
|
style helps make this stand out from other indented scenarios. By avoiding braceless
|
|
subordinates to control structures we avoid a class of silly maintenance bugs.
|
|
|
|
|
|
12) Initializer lists shall be treated as-if they're part of the ctor body. They get
|
|
single indented, unless the function is really short.
|
|
|
|
Example:
|
|
|
|
struct StringWrapper
|
|
{
|
|
std::string string;
|
|
|
|
explicit
|
|
StringWrapper( const std::string &s )
|
|
: string( s ) {}
|
|
|
|
explicit StringWrapper() : string( "no value" ) {} // A short ctor or other function can be on one-line.
|
|
};
|
|
|
|
Exceptions: Single line functions and ctors, and the like are sometimes good to conserve space. Trivial
|
|
implementations should take trivial amounts of space.
|
|
|
|
|
|
13) Private data shall be declared before the functionality of the class is declared or defined. Constructors,
|
|
copy assignment, and destructors shall be declared (or defined) before other member functions.
|
|
|
|
Example:
|
|
|
|
template< typename T >
|
|
class smart_ptr : boost::noncopyable
|
|
{
|
|
private:
|
|
T *ptr;
|
|
|
|
public:
|
|
explicit inline
|
|
smart_ptr( T *const p )
|
|
: ptr( p ) {}
|
|
|
|
inline ~smart_ptr() { delete p; }
|
|
|
|
T &
|
|
operator *() const
|
|
{
|
|
return *ptr;
|
|
}
|
|
|
|
T *
|
|
operator->() const
|
|
{
|
|
return ptr;
|
|
}
|
|
};
|
|
|
|
Reasons: It's easier to reason about the correctness of a class's primary invariant mechanism (construction
|
|
and destruction) when its near the variables and types it manages. Knowing the contents of a class helps to
|
|
understand about the invariants which must be maintained. Some argue this makes finding the public interfaces
|
|
harder -- but Hera classes are designed to be perused by doxygen, and thus this documentation shall serve as
|
|
a primary public usage reference. Within Hera, the only reason to read a header file is to understand more
|
|
about a particular implementation.
|
|
|
|
Exceptions: None.
|
|
|
|
|
|
14) Class parents (inheritance) shall logically be on the same line as the declaration. If on another line,
|
|
must count as a continuation.
|
|
|
|
Reasons: It helps to isolate a class's name from the parent list, which is (when you think about it) part of
|
|
the class, and needs indentation.
|
|
|
|
|
|
15) Preprocessor nested scopes (which sometimes do happen) shall indent, by requisite spacing, all characters
|
|
AFTER the leading "#".
|
|
|
|
Examples:
|
|
|
|
#ifndef SOME_THING
|
|
# ifndef SOME_OTHER_THING
|
|
# define NEITHER_THING_IS_AVAILABLE
|
|
# else
|
|
# error Must have SOME_THING if SOME_OTHER_THING is around
|
|
# endif
|
|
#endif
|
|
|
|
Reasons: Because C preprocessor macros also follow logical scope, it helps to trace their nesting by indentation.
|
|
By indenting after the leading "#", we help distinguish C++ code from C preprocessor code.
|
|
|
|
Exceptions: The header guards, being ubiquitous, shall not count as a level of indentation in nested preprocessor
|
|
construct
|
|
|
|
|
|
16) In declaration of entities, the following order of keywords shall always be followed:
|
|
|
|
virtual explicit extern static constexpr consteval constinit inline mutable const volatile <TYPE>
|
|
|
|
For integers & double:
|
|
unsigned long long int
|
|
signed short int
|
|
long double
|
|
|
|
signed and int are each optional.
|
|
|
|
For characters:
|
|
unsigned char
|
|
signed char
|
|
char
|
|
|
|
Each of those char types are three distinct types -- signed or unsigned are NOT optional.
|
|
|
|
|
|
Examples:
|
|
struct Type
|
|
{
|
|
virtual inline ~Type() noexcept {} // Yes, this is actually allowed.
|
|
explicit inline Type() {}
|
|
static inline const std::string string() { return "String"; }
|
|
|
|
// Allowed, but violates the style guide:
|
|
long const unsigned volatile int virtual long inline *resource() const;
|
|
|
|
// The same declaration, following the style guide:
|
|
virtual inline const volatile unsigned long long int *resource() const;
|
|
};
|
|
|
|
Reasons: C++ is very forgiving in the order of its type-and-qualifiers, but standardizing them makes finding
|
|
specific things simpler. There are many mutually exclusive keywords above, like static and virtual and explicit,
|
|
for example. Many may not know it but 'long const unsigned volatile int virtual long inline *' is a valid token
|
|
sequence for declaring a member function. Standardization gives us: 'virtual inline const volatile unsigned
|
|
long long int *', which makes the declaration type far less confusing.
|