-
Notifications
You must be signed in to change notification settings - Fork 0
Exceptions
C and C++ exceptions are prohibited in this codebase.
This is a deliberate, permanent design choice based on clarity, predictability, and maintainability.
Exceptions introduce hidden control flow, implicit contracts, and non‑local side effects that make code harder to reason about, harder to debug, and harder to guarantee across platforms, compilers, and build configurations. They also impose runtime and binary‑size costs that are unacceptable in performance‑sensitive or real‑time environments.
By removing exceptions entirely, we ensure that:
- all control flow is explicit
- all error paths are visible at the call site
- all contracts are documented and enforced intentionally
- behaviour is consistent across compilers, platforms, and optimisation levels
- debugging never involves invisible stack‑unwinding or half‑executed states
Codebases are to use explicit return types, status objects, and well‑defined error‑handling patterns. These mechanisms make failure modes predictable, testable, and easy to follow - without relying on hidden language machinery.
This is not a stylistic preference. This is a foundational architectural rule.
Exceptions should ideally be disabled, marked unsupported, and generally not permitted under any circumstances (outside of required dealings with external libraries and native code).
Since exceptions are not permitted, all error handling must be explicit, visible at the call site, and part of the function’s contract. The following patterns are approved and encouraged.
struct Status
{
bool ok = true;
std::string message;
};
Status loadConfig (const juce::File& file); ...or even better with JUCE, use juce::Result:
juce::Result loadConfig (const juce::File&); Usage:
if (const auto result = loadConfig (file); status.failed())
{
logError (status.getErrorMessage());
return;
} std::optional<User> findUser (UserID);Usage:
if (auto user = findUser (id))
process (*user);
else
handleMissingUser (id);using LoadResult = std::expected<Config, std::string>;
LoadResult loadConfig(const File& file);Usage:
auto result = loadConfig(file);
if (! result)
{
logError(result.error());
return;
}
applyConfig(*result);enum class ParseError
{
none,
invalidFormat,
missingField,
outOfRange
};
ParseError parse (const std::string& text, Data& out);Usage:
bool doImportantStuff (const std::string& text, DataType& dataOut)
{
switch (parse (text, dataOut))
{
case ParseError::InvalidFormat: return handleInvalidFormat();
case ParseError::MissingField: return handleMissingField();
case ParseError::OutOfRange: return handleOutOfRange();
case ParseError::None: break;
default: jassertfalse; break;
}
return true;
}void myFunc (...)
{
jassert (buffer != nullptr);
jassert (size > 0);
}