Style Guide - Huntr Software LLC

Last Updated: November 1, 2026

This guide is maintained with AI-assistance. Please refer to our AI policy.

Read through this guide to learn how C++ code is styled for Huntr Software projects, and how docs, changelogs, and other project writing should read. Each project's CONTRIBUTING.md holds its own conventions (type prefixes, namespaces, export macros, the copyright header), while this guide covers what they share.

New code follows this guide. Older code that doesn't conform should be updated one file at a time, in a commit of its own, never mixed with a behavior change. Until a file is converted, edits inside it should match the surrounding code.

Formatting

  • Tabs for indentation.
  • Opening braces go on their own line: types, functions, namespaces, and control blocks.
  • A single-statement body may drop its braces, with the statement on its own line.
  • A trivial definition can stay on one line.
  • A value within a braced initializer have spaces on each side, like bool expanded{ false }.
  • Wrap lines at about 80 columns (a tab counts as 4). A continuation line gets one extra tab.
  • A line broken at a binary operator (&&, ||, +, ==, ...) ends with the operator, rather than the next line starting with it.
  • * and & bind to the type: LLayer* layer, const LString& path.
LLayer* find_or_create(LLayer* parent, const LString& name)
{
    if (LLayer* existing = parent->find_layer(name))
        return existing;

    return lMake<LLayer>(parent, name);
}

LEngine::LEngine() : pimpl{ new Impl() } {}
LEngine::~LEngine() { delete pimpl; }

Names

  • Types are PascalCase behind the project's prefix (LEngine).
  • Functions, methods, variables, parameters, and constants are snake_case. Constants take no prefix (version_token, not kVersionToken).
  • Macros, include guards among them, are the one ALL_CAPS name, since they ignore scope.
  • Private and protected data members take an m_ prefix (m_tokens), so they never clash with a parameter of the same name. Public struct fields don't.
  • No type prefixes such as b for booleans. Name a boolean so it reads as yes or no: expanded, is_open, has_file.
  • enum class values are PascalCase.
  • A name says what something is, not when or why it was added: no new_, old_, v2, or plan and slice numbers.

Headers and includes

  • Include guards rather than #pragma once: #ifndef LENGINE_H closing with #endif // LENGINE_H.
  • Includes come in groups separated by blank lines, alphabetical within each: in a .cpp, its own header first, then the standard library, then project headers.
  • File-local helpers are static functions in the .cpp.

Comments and documentation

Documentation lives in headers, and a .cpp stays clean.

  • Document public API with a /** */ block directly above the declaration. Each line starts with *, and text sits two spaces after the star. Editors show these blocks on hover, and Docuriser strips the stars and renders the rest as Markdown: backticks for identifiers, cpp code blocks for examples, ### headings to section a long class doc.

    /**
     *  Finds a layer by its path.
     *
     *  Returns `nullptr` when no layer matches.
     */
    LLayer* find_layer(const LString& path);
    
  • Open with one sentence. A function starts with a verb ("Finds a layer by its path."), a type with what it is ("Runtime engine that loads .lyr files..."). Then add what a caller needs: return values, failure behavior, ordering requirements.

  • Doc comments follow the Writing rules below, like any other project writing.

  • Small members (enum values, struct fields) take a trailing // comment when it fits on the line within about 90 columns. Anything longer gets a /** */ block above the member instead.

  • In comments and docs, acronyms keep their capitals (URL, JSON, HTML), while identifiers stay snake_case (url).

  • In a .cpp, comment only what the code can't say for itself, and say why rather than what:

    // States are child attributes, so the recursive walk covers them too.
    for (LAttribute* attr : pimpl->root->find_children<LAttribute>(true))
    
  • No walls of text. A comment that needs several paragraphs belongs in the header docs or a guide.

  • Comments describe the code as it is now: no "used to", "was changed to", "no longer", references to removed code, or associated plan steps. History belongs in commit messages and CHANGELOG.md.

Tests

  • Name a test for what it covers (test-cycles, test-lexer), never for the plan or change that produced it.

C++ idioms

  • nullptr, never NULL or 0.
  • auto only where the type is obvious or verbose (iterators, range-for elements); spell it out otherwise.
  • Pass non-trivial types by const&, and mark methods that don't mutate const.
  • enum class over plain enum.
  • A type that can't be copied deletes its copy operations explicitly, with a doc comment saying why.

Other languages

For JavaScript, CMake, shell, and the rest, match the style of the surrounding code. The comment rules above apply everywhere.

Writing

These rules cover all project writing: doc comments, guides, READMEs, changelogs, and site text.

  • Prefer plain words over jargon.
  • Before relying on a new term, say how it connects to the ones already in use.
  • When the shape of some data isn't obvious (nested containers, lists that line up with other lists), show an example value.
  • Backticks mark names that exist in the code, never made-up placeholders.
  • No em-dashes. Use commas, colons, parentheses, or separate sentences instead.
  • Keep "that" in relative clauses: "the files that the build produces", not "the files the build produces".
  • "Link" takes "to": "the files that the site links to", not "the files the site links".
  • Avoid stacked plurals with the verb at the end, like "the pages that projects' sidebars link to". Make part of it singular ("the pages that a project's sidebar links to") or restructure the sentence.
  • A phrase reused from a parallel section must still be true in its new place.

Changelogs

  • A change that users will notice gets its CHANGELOG.md entry in the same commit as the change, under ## [?] - Unreleased at the top until the release's version is set.
  • Group entries under ### Added, ### Changed, ### Fixed, and ### Removed. Add ### Migration when users must act, or ### Recommended when the old way still works but a new one is preferred.
  • Internal work that users won't notice, like a helper that nothing calls yet, needs no entry.
Appearance
Theme
—