KSS - Coding Standards

‍General ‍Coding ‍Principles

  • ‍1. ‍Be ‍very ‍familiar ‍with ‍[C++ ‍Core ‍Guidelines]. ‍If ‍you ‍choose ‍to ‍write ‍code ‍that ‍breaks ‍these ‍guidelines, ‍be ‍prepared ‍to ‍justify ‍your ‍decision.
  • ‍2. ‍Favour ‍reliability ‍and ‍readability ‍over ‍strict ‍formatting ‍guidelines, ‍but ‍if ‍you ‍choose ‍to ‍break ‍one ‍of ‍our ‍guidelines ‍you ‍must ‍be ‍prepared ‍to ‍justify ‍your ‍decision.
  • ‍3. ‍Follow ‍[Programming ‍by ‍Contract] ‍patterns. ‍The ‍[ksscontract] ‍library ‍contains ‍the ‍utilities ‍we ‍use ‍for ‍this ‍purpose.
  • ‍4. ‍Write ‍unit ‍tests ‍that ‍cover ‍your ‍code. ‍Your ‍code ‍must ‍pass ‍the ‍unit ‍tests ‍before ‍your ‍pull ‍requests ‍will ‍be ‍accepted. ‍The ‍[ksstest] ‍library ‍contains ‍the ‍utilities ‍we ‍use ‍for ‍this ‍purpose.
  • ‍5. ‍Follow ‍the ‍C++14 ‍standards. ‍(We ‍will ‍switch ‍to ‍the ‍C++17 ‍standard ‍in ‍2020.)
  • ‍6. ‍Don’t ‍work ‍on ‍the ‍master ‍branch, ‍or ‍on ‍the ‍development ‍branches. ‍Create ‍your ‍own ‍suitably ‍named ‍branch ‍from ‍the ‍development ‍branch ‍on ‍which ‍to ‍do ‍your ‍work.
  • ‍7. ‍Eliminate ‍all ‍compiler ‍warnings. ‍If ‍you ‍absolutely ‍cannot ‍eliminate ‍a ‍warning, ‍then ‍suppress ‍it ‍using ‍the ‍appropriate ‍#pragma, ‍but ‍be ‍prepared ‍to ‍justify ‍your ‍decision.

‍Header ‍Files

  • ‍1. ‍Header ‍files ‍must ‍be ‍protected ‍using ‍#if ‍defined(kss<library>_<filename>_<extension>). ‍
  • ‍2. ‍Everything ‍in ‍the ‍header ‍files ‍should ‍be ‍in ‍the ‍namespace ‍kss::<library> ‍or ‍a ‍sub-space ‍thereof.
  • ‍3. ‍Items ‍in ‍the ‍header ‍files ‍that ‍should ‍be ‍considered ‍private, ‍but ‍for ‍technical ‍reasons ‍cannot ‍be ‍private, ‍should ‍be ‍placed ‍in ‍the ‍namespace ‍kss::<library>::_private.
  • ‍4. ‍Header ‍file ‍contents ‍do ‍not ‍have ‍to ‍be ‍limited ‍to ‍a ‍single ‍class, ‍but ‍everything ‍in ‍them ‍should ‍be ‍closely ‍related.
  • ‍5. ‍Try ‍to ‍keep ‍implementation ‍details ‍out ‍of ‍the ‍header ‍files ‍exception ‍for ‍template ‍code ‍and ‍very ‍simple ‍classes ‍and ‍structures. ‍Make ‍use ‍of ‍the ‍[PIMPL ‍Pattern] ‍when ‍suitable.
  • ‍6. ‍Include ‍documenting ‍comments ‍on ‍the ‍public ‍portion ‍of ‍your ‍API. ‍Use ‍a ‍syntax ‍that ‍will ‍work ‍for ‍both ‍Doxygen ‍and ‍Xcode’s ‍documentation.
  • ‍7. ‍Do ‍not ‍have ‍use ‍namespace ‍<whatever> ‍in ‍your ‍header ‍files ‍except ‍where ‍they ‍are ‍reasonably ‍limited ‍in ‍scope. ‍

‍Source ‍Files

  • ‍1. ‍Indent ‍your ‍code ‍in ‍the ‍usual ‍places ‍using ‍4 ‍spaces ‍(no ‍tabs ‍except ‍where ‍required).
  • ‍2. ‍Keep ‍lines ‍to ‍no ‍more ‍than ‍95 ‍characters ‍in ‍length. ‍(Our ‍code ‍reviewers ‍should ‍be ‍able ‍to ‍view ‍two ‍files ‍side ‍by ‍side ‍in ‍a ‍12pt ‍font ‍on ‍a ‍reasonably ‍sized ‍desk ‍monitor.)
  • ‍3. ‍Keep ‍methods ‍and ‍blocks ‍to ‍no ‍more ‍than ‍50 ‍lines ‍in ‍length. ‍(Developers ‍should ‍be ‍able ‍to ‍see ‍an ‍entire ‍block ‍at ‍once ‍in ‍a ‍12pt ‍font ‍on ‍a ‍laptop.)
  • ‍4. ‍Prefer ‍the ‍use ‍of ‍anonymous ‍namespaces ‍of ‍the ‍static ‍keyword.
  • ‍5. ‍When ‍including ‍header ‍files ‍group ‍them ‍in ‍the ‍order ‍of ‍standard ‍C++ ‍libraries ‍(e.g. ‍#include ‍<string>), ‍standard ‍OS ‍libraries ‍(e.g. ‍#include ‍<sys/wait.h>), ‍third ‍party ‍libraries ‍(e.g. ‍#include ‍<curl/curl.h>), ‍then ‍finally ‍local ‍includes ‍(e.g. ‍#include ‍"rolling_file.hpp").
  • ‍6. ‍Local ‍includes ‍should ‍use ‍the ‍form ‍#include ‍"filename" ‍while ‍all ‍others ‍should ‍use ‍the ‍form ‍#include ‍<filename>.
  • ‍7. ‍Use ‍using ‍namespace ‍…, ‍namespace ‍= ‍…, ‍and ‍use ‍something_t ‍= ‍… ‍to ‍reduce ‍the ‍need ‍to ‍explicitly ‍refer ‍to ‍namespaces ‍in ‍your ‍code.
  • ‍8. ‍Don’t ‍repeat ‍the ‍documentation ‍comments ‍found ‍in ‍your ‍header ‍files ‍in ‍your ‍source ‍files. ‍Note ‍that ‍our ‍Doxygen ‍configurations ‍ignore ‍the ‍source ‍files ‍on ‍the ‍assumption ‍that ‍only ‍the ‍header ‍files ‍are ‍necessary ‍to ‍document ‍the ‍API.
  • ‍9. ‍Don’t ‍comment ‍the ‍obvious. ‍It ‍should ‍be ‍possible ‍to ‍determine ‍what ‍your ‍code ‍does ‍by ‍examining ‍the ‍code ‍itself. ‍Comments ‍should ‍primarily ‍be ‍used ‍to ‍describe ‍why ‍you ‍chose ‍a ‍specific ‍implementation, ‍or ‍why ‍you ‍are ‍breaking ‍from ‍one ‍of ‍our ‍standards, ‍or ‍just ‍to ‍make ‍the ‍code ‍more ‍navigable. ‍(e.g. ‍// ‍MARK: ‍<something> ‍and ‍// ‍TODO: ‍<something>)

‍Naming ‍Conventions

  • ‍1. ‍Header ‍filenames: ‍C++ ‍header ‍files ‍should ‍use ‍the ‍extension ‍.hpp ‍and ‍be ‍all ‍lower ‍case. ‍C ‍header ‍files ‍should ‍use ‍the ‍extension ‍.h ‍and ‍C/C++ ‍mixed ‍header ‍files ‍should ‍use ‍the ‍extension ‍.h ‍and ‍include ‍appropriate ‍#if ‍defined(__cplusplus) ‍statements. ‍Header ‍files ‍that ‍should ‍not ‍be ‍installed ‍with ‍the ‍library ‍(i.e. ‍that ‍are ‍used ‍only ‍internally ‍while ‍compiling) ‍should ‍begin ‍with ‍a ‍single ‍underscore.
  • ‍2. ‍Source ‍filenames: ‍C++ ‍source ‍files ‍should ‍use ‍the ‍extension ‍.cpp, ‍be ‍all ‍lower ‍case, ‍and ‍should ‍match ‍their ‍.hpp. ‍C ‍source ‍files ‍should ‍use ‍the ‍extension ‍.c, ‍be ‍all ‍lower ‍case, ‍and ‍should ‍match ‍their ‍.h.
  • ‍3. ‍Namespaces: ‍all ‍lower ‍case ‍and ‍single ‍word.
  • ‍4. ‍Classes: ‍CamelCase ‍except ‍for ‍certain ‍cases ‍where ‍we ‍want ‍to ‍make ‍them ‍look ‍like ‍a ‍keyword.
  • ‍5. ‍Variables ‍(local ‍and ‍member): ‍camelCase.
  • ‍6. ‍Member ‍variables ‍that ‍would ‍conflict ‍with ‍a ‍method ‍name: ‍_underscorePrefixedCamelCase.
  • ‍7. ‍Type ‍aliases: ‍lowercase_t ‍(e.g. ‍use ‍requestlist_t ‍= ‍list<Request>;) ‍Use ‍_fn ‍instead ‍of ‍_tif ‍the ‍type ‍is ‍a ‍function.
  • ‍8. ‍Macros: ‍ALL_UPPERCASE ‍- ‍but ‍keep ‍macros ‍scarce. ‍Work ‍hard ‍to ‍avoid ‍them ‍if ‍possible,and ‍be ‍prepared ‍to ‍justify ‍their ‍use.

‍Braces

‍Always ‍use ‍braces ‍in ‍decision ‍and ‍looping ‍constructs. ‍For ‍single ‍line ‍items ‍these ‍may ‍be ‍on ‍the ‍same ‍line, ‍but ‍do ‍not ‍have ‍to ‍be. ‍For ‍multiple ‍line ‍items ‍they ‍should ‍not ‍be ‍combined ‍on ‍the ‍same ‍line. ‍Also ‍don't ‍put ‍the ‍opening ‍brace ‍on ‍a ‍separate ‍line, ‍unless ‍the ‍statement ‍requires ‍more ‍than ‍one ‍line.

‍The ‍following ‍are ‍acceptable:

‍    if ‍(somethingIsTrue) ‍{ ‍...do ‍something... ‍}


‍    if ‍(somethingIsTrue) ‍{

‍        ...do ‍something... ‍

‍    }


‍    if ‍(somethingIsTrue ‍

‍        && ‍thisVariable ‍== ‍"hello ‍world"

‍        && ‍userHasPermissionToDoThis(action))

‍    {

‍        ...do ‍something...

‍    }


‍The ‍following ‍are ‍not ‍acceptable:

‍    if ‍(somethingIsTrue) ‍doSomething();


‍    if ‍(somethingIsTrue) ‍{ ‍doSomething(); ‍doSomethingElse(); ‍}


‍    if ‍(somethingIsTrue)

‍    {

‍        ...do ‍something...

‍    }

‍Whitespace

‍Do ‍not ‍use ‍newlines ‍liberally. ‍In ‍particular, ‍do ‍not ‍use ‍them ‍to ‍make ‍your ‍code ‍"double ‍spaced." ‍Newlines ‍should ‍be ‍used ‍to ‍draw ‍the ‍eye ‍to ‍logically ‍grouped ‍sections ‍of ‍the ‍code.

    /*!

     The RollingFile class writes files with a maximum size. When the size is passed, the

     file is closed and a new file is opened. Note that the filenames will be of the form


     @code

     <fileNamePrefix><counter>[.<fileNameSuffix>]

     @endcode


     where counter is automatically generated, starting with 000001. If the value

     exceeds 999999, the RollingFile will continue to work, but the filenames will

     grow longer.

     */

    class RollingFile {

    public:


        /*!

         Start a rolling file with the given prefix, suffix (extension), and maximum,

         file size. Note that the file is created here but will be empty until the

         first write takes place.


         @param fileWrapSize a new file is started after a write takes the size over this

         @param fileNamePrefix the prefix, including path, of the files to be written

         @param fileNameSuffix if provided, will be appended after a "dot"

         @throws std::invalid_argument if fileWrapSize is not positive or if

            fileNamePrefix is empty.

         */

        RollingFile(std::streamsize fileWrapSize,

                    const std::string& fileNamePrefix,

                    const std::string& fileNameSuffix = "");