Code Style Guide
Symbol prefix
Public symbols should be prefixed with fg_
to prevent clashes with other
libraries as C does not have namespaces.
Include guard
Include guards should be prefixed with FORGE_
. The final #endif
statement
should be followed by a trailing comment to indicate the corresponding
identifier, for example:
#ifndef FORGE_FOO_H #define FORGE_FOO_H // ... #endif // FORGE_FOO_H
Header includes
Translation units and their dependencies are governed by a simple rule: header files may only include header files that declare common types. This is similar in spirit to OurMachinery but less stringent. The primary goal with this design is to keep data and functions operating on said data separate.
TODO: Elaborate on design rationale.
License header
Forge software is, and always will be, committed to being free. Source files should begin with the following license header:
// This file is part of Forge, the foundation library for Forge tools. // // Copyright (C) <year> <author name> <author email> // SPDX-License-Identifier: GPL-3.0-only // // This Source Code Form is subject to the terms of the GNU General Public // License v3.0 only. You should have received a copy of the license along with // this program. If not, see <https://www.gnu.org/licenses/>.
Example copyright statement:
// Copyright (C) 2021 Ryan Chan <ryancwo@posteo.net>
Documentation
Documentation should be written in Markdown with additional support for
equations written in LaTeX (equations must be delimited by \\(
and
\\)
in order to be parsed correctly). There are no special commands
or conventions. Header overviews are the only exception as there is
no built-in support for parsing overview comments in libclang. Overviews
must begin with the line:
// module: <module name>
Example snippet from forge/gui.h
:
/* * module: forge/gui.h * This header should only be included in definition (.c) files. */
Single vs. double-precision
fg_real
should be used instead of float
or double
in places where
floating-point precision may vary. The FG_REAL_64
preprocessor definition may
be used to toggle between the two types at compile-time.
Preprocessor macros
Preprocessor macros are bug-prone:
- they cannot be debugged;
- lack type information; and
- can produce side effects as a result of unintuitive expansion rules.
Compilers are effective at inlining functions automatically (although inline
can be used to force inlining) and variables are often better expressed with
type information.
// Good int const foo = 32; // Bad #define FOO 32
// Good float mult(float lhs, float rhs) { return lhs * rhs; } // Bad #define mult(lhs, rhs) lhs * rhs
Using macros for code generation (e.g. linmath.h, eee...) is
discouraged. tmplgen
is the preferred tool for program text
manipulation:
- the generated code is predictable;
- basic control structures can be used to produce exact/tailored code; and
- the resulting code can be debugged normally!
If macros must be used, refer to the Linux kernel coding style for best practices.