Batteries C++ Coding Style Guide
Batteries C++ Coding Style Guide🔗
This document describes the coding conventions to be followed in this library. Generally, the coding style Batteries uses is modelled after the Google C++ Style Guide. Generally, we also recommend following the advice in the C++ Core Guidelines, except when they contradict the Google style or this document. In summary, the order of precedence is:
Some of the major points:
- Avoid Exceptions (see also <batteries/status.hpp> docs)
- Always clang-format the code, using the
.clang-format
file at the top of the Batteries repo - Optimize for the reader, not the writer
The rest of this document is a collection of call-outs that the Batteries team has found helpful in onboarding new developers and getting them up to speed on good Batteries C++ style.
Casting and Type Conversions🔗
Prefer explicit constructor calls over static_cast
when possible🔗
For example, std::string
has an implicit constructor(9) that takes a StringViewLike
object (which of course includes std::string_view
. Therefore it is possible to write:
DO NOT write:
However, this is suboptimal for the following reason: because we prioritize ease-of-reading over ease-of-writing, we care a great deal about the cognitive effort it takes for a reader to mentally parse and understand the meaning of a piece of code. Casts, in general, should be avoided when possible, since they open the door to all kinds of undefined and unsafe behavior. Therefore, when a reader sees a cast, they should immediately start paying closer attention, since the stakes of misunderstanding have just risen significantly. The example above does not really warrant this extra expendature of mental effort, so we prefer writing it without the cast:
Instead, DO write:
or, even simpler:
Class Member Access🔗
To enhance readability, all implicit uses of this
within a class should be made explicit. For example:
DO NOT write:
Instead, DO write:
Environment Variables🔗
Use batt::getenv_as<T>
to access environment variables🔗
This is preferred over calling getenv
and std::getenv
directly because batt::getenv_as
automatically handles type conversion/parsing (and parse errors), and forces the calling code to deal with a variable being undefined or incorrectly formatted by returning an optional
type.
DO NOT write:
Instead, DO write:
Error Handling: Status, CHECK, ASSERT, and throw🔗
Prefer CHECK/ASSERT to Status/throw only for problems in the code itself🔗
A good test to apply when trying to decide whether to use a CHECK or ASSERT statement, which will abort the program on failure, is whether the error being detected can be corrected in principle only by a coding change to the program. In other words, if it represents something like bad input, resource exhaustion, data corruptions, etc., then CHECK/ASSERT is the wrong mechanism to use. If however, a certain condition should always be true if the programmer's intent is correctly implemented, then CHECK/ASSERT is the way to go.
Use Status internally within Batteries/project code🔗
- Functions that would otherwise return
void
should returnbatt::Status
- Functions that would otherwise return some other type
R
should returnbatt::StatusOr<R>
- Declare functions as
noexcept
when returningbatt::Status
orbatt::StatusOr
Use BATT_THROW_IF_NOT_OK
to implement interfaces that use exceptions🔗
The use of some third party libraries together with Batteries in a larger application may dictate writing free functions or class/struct member functions that report errors by throwing. Use batt::StatusException
at the boundary layer only, and use macros like BATT_THROW_IF_NOT_OK
and BATT_OK_RESULT_OR_THROW
wherever relevant.
See <batteries/exception.hpp> for more details.
File Names🔗
All source code files live under batteries/src
.
- Source, header, inline/implementation, and test sources should be colocated within the same directory.
- Source files should use the
.cpp
extension. - Header files should use the
.hpp
extension. - Test files for a given source/header should use the
.test.cpp
suffix.
For example, if you have a header file: src/some_namespace/myutils.hpp
, then you should also have:
src/some_namespace/myutils.cpp
src/some_namespace/myutils.test.cpp
src/some_namespace/myutils.ipp
Integer Scalar Types🔗
Please use the type aliases defined in <batteries/int_types.hpp> instead of their built-in names or the more verbose aliases in the standard header <cstdint>.
DO NOT write:
Instead, DO write: