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 <>.

Example copyright statement:

//  Copyright (C) 2021 Ryan Chan <>


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:

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:

If macros must be used, refer to the Linux kernel coding style for best practices.