C++ coding standard
These rules are the host-code baseline for new UWHPC C++ projects. The examples are copied from VMC; links below each example point to the exact source lines.
Language and diagnostics
Section titled “Language and diagnostics”- Must: compile new host code as C++23 or newer.
- Must: require the selected language level rather than silently accepting an older compiler mode.
- Must: enable
-Wall -Wextra -Wpedantic -Wshadow -Wconversion -Wsign-conversionon GCC and Clang, or/W4 /permissive-on MSVC. - Should: generate
compile_commands.jsonfor editor and static-analysis tooling. - Must: keep warning and optimization flags target-scoped.
VMC sets the required standard and warning policy in
CMakeLists.txt
and
CMakeLists.txt.
Its target-scoped application is visible in
target_compile_options.
Formatting
Section titled “Formatting”- Must: check a
.clang-formatfile into the repository. - Must: use two-space indentation and spaces rather than tabs.
- Must: keep code within 100 columns after formatting.
- Must: sort includes and attach
*and&to the type. - Should: run
clang-formaton changed C++, header,.cu, and.cuhfiles before review.
VMC example:
BasedOnStyle: LLVMStandard: LatestColumnLimit: 100IndentWidth: 2UseTab: NeverPointerAlignment: LeftAlignConsecutiveMacros: trueSortIncludes: trueAccessModifierOffset: -2Source: VMC .clang-format.
Names and file layout
Section titled “Names and file layout”- Must: use
PascalCasefor classes, structs, and enums. - Must: use
snake_casefor functions and ordinary variables. - Must: suffix private data members with
_. - Should: keep declarations in
.hppor.cuhand non-template definitions in.cppor.cu. - Should: place private implementation helpers in an unnamed namespace in the implementation file.
- May: retain short conventional mathematical names such as
L,N,i, andjwhen their scope is small and their meaning is standard in the local algorithm.
VMC example:
class Simulation {private: Config config_;
Particles particles_; WaveFunction wave_function_; BlockingAnalysis blocking_analysis_; EnergyTracker energy_tracker_; std::unique_ptr<OutputWriter> output_writer_;Source: src/simulation/simulation.cuh.
Initialization and type safety
Section titled “Initialization and type safety”- Must: initialize objects and scalars; use brace initialization by default.
- Must: use fixed-width integer types for serialized or externally defined binary fields.
- Should: use
std::size_tfor sizes and indexes into in-memory containers. - Must: use explicit casts when crossing numeric domains; do not rely on a narrowing implicit conversion.
- Must: use
constfor values that do not change after initialization. - Should: centralize project-wide numerical precision behind a type alias when both FP32 and FP64 builds are supported.
VMC example:
#ifdef FP_64using real_t = double;#elseusing real_t = float;#endif
using complex_t = std::complex<real_t>;Source: src/utilities/macros.cuh.
The VMC parser makes conversion points explicit:
if constexpr (std::is_signed_v<T>) { out = static_cast<T>(std::stoll(it->second));} else { out = static_cast<T>(std::stoull(it->second));}Source: src/config/config.hpp.
Interfaces and ownership
Section titled “Interfaces and ownership”- Must: use
expliciton a single-argument constructor unless implicit conversion is intentional and documented. - Must: mark a result
[[nodiscard]]when ignoring it is likely a defect. - Must: express exclusive ownership with an RAII owner such as
std::unique_ptr. - Must: use
std::optional<T>when “no value yet” or “not available” is a normal result, rather than a magic sentinel. - Should: make query functions
constand usenoexceptwhere the contract truly forbids exceptions. - Must: give polymorphic base classes a virtual destructor.
VMC example:
struct MeasurementSummary { real_t mean_energy; std::optional<real_t> standard_error; real_t acceptance_rate;};
explicit Simulation(Config cfg, std::unique_ptr<OutputWriter> output_writer = nullptr);Source: src/simulation/simulation.cuh.
The output interface demonstrates virtual destruction and explicit overrides:
class OutputWriter {public: virtual ~OutputWriter() = default;
virtual void write_init(const InitData& data) = 0; virtual void write_frame(const FrameData& data) = 0; virtual void write_done(const DoneData& data) = 0;};Source: src/output_writer/output_writer.hpp.
Errors and validation
Section titled “Errors and validation”- Must: validate configuration and public API preconditions at their input boundary.
- Must: report invalid host-side input with a specific exception or a project-wide error type.
- Must: never ignore an error code from a runtime or numerical library.
- Should: include the invalid field and required constraint in an error message.
- Must: not throw across a CUDA kernel boundary; CUDA failures follow the device/runtime policy in the CUDA standard.
VMC example:
if (num_particles < 1U) { throw std::invalid_argument("[Config] Num_Particles must be >= 1");}if (box_length <= 0.0_r || !std::isfinite(box_length)) { throw std::invalid_argument("[Config] Box_Length must be finite and > 0");}Source: src/config/config.hpp.
- Must: test each public component’s normal path, boundary cases, and error path.
- Must: give tests behavior-oriented names and subsystem tags.
- Must: make stochastic tests reproducible with explicit seeds.
- Must: test scientific or numerical invariants independently of testing mere execution.
- Should: compare floating-point results with a named tolerance appropriate to the precision mode.
VMC’s simulation tests validate record counts, rates, optional results, and
edge cases in
tests/test_simulation.cpp.
Its rigorous suite independently checks a near-contact analytic result in
tests/test_rigorous_physics.cpp.