pw_status#
Exception-free error propagation for embedded
Stable C++ C Python Java TypeScript Rust
Easy: Simple to understand, includes convenient macro PW_TRY
Efficient: No memory allocation, no exceptions
Established: Just like
absl::Status, deployed extensively at GoogleCustomizable: Create custom status types for domain-specific error codes with pw_enum
pw::Status is Pigweed’s error propagation primitive, enabling
exception-free error handling. The primary feature of pw_status is the
pw::Status class, a simple, zero-overhead status object that
wraps a status code, and the PW_TRY macro. For example:
#include "pw_status/status.h"
pw::Status ImuEnable() {
if (!device_has_imu) {
return Status::FailedPrecondition();
}
PW_TRY(ImuSpiSendEnable()); // Propagates failure on non-OK status.
return pw::OkStatus();
}
void Initialize() {
if (auto status = ImuEnable(); status.ok()) {
PW_LOG_INFO("Imu initialized successfully")
} else {
if (status.IsFailedPrecondition()) {
PW_LOG_WARNING("No IMU present");
} else {
PW_LOG_ERROR("Unknown error initializing IMU: %d", status.code());
}
}
}
pw_status provides an implementation of status in every supported
Pigweed language, including C, Rust, TypeScript, Java, and Python.
Pigweed’s status matches Google’s standard status codes (see the Google APIs repository). These codes are used extensively in Google projects including Abseil (status/status.h) and gRPC (doc/statuscodes.md).
Integrate pw_status into your project, see common uses
Detailed description of pw_status’s methods.
Quick reference#
See Status codes for the precise semantics of each error, as well as how to spell the status in each of our supported languages. Click on the status names below to jump directly to that error’s reference.
Status |
Code |
Description |
|---|---|---|
0 |
Operation succeeded |
|
1 |
Operation was cancelled, typically by the caller |
|
2 |
Unknown error occurred. Avoid this code when possible. |
|
3 |
Argument was malformed; e.g. invalid characters when parsing integer |
|
4 |
Deadline passed before operation completed |
|
5 |
The entity that the caller requested (e.g. file or directory) is not found |
|
6 |
The entity that the caller requested to create is already present |
|
7 |
Caller lacks permission to execute action |
|
8 |
Insufficient resources to complete operation; e.g. supplied buffer is too small |
|
9 |
System isn’t in the required state; e.g. deleting a non-empty directory |
|
10 |
Operation aborted due to e.g. concurrency issue or failed transaction |
|
11 |
Operation attempted out of range; e.g. seeking past end of file |
|
12 |
Operation isn’t implemented or supported |
|
13 |
Internal error occurred; e.g. system invariants were violated |
|
14 |
Requested operation can’t finish now, but may at a later time |
|
15 |
Unrecoverable data loss occurred while completing the requested operation |
|
16 |
Caller does not have valid authentication credentials for the operation |
Custom status codes with StatusBase#
pw::StatusBase is a base class template that provides Pigweed status
semantics for custom code types, such as domain-specific error enums or integer
codes. It provides ok() checks, [[nodiscard]] enforcement, and error
propagation with PW_TRY and pw::Result. Pigweed’s standard
pw::Status is itself implemented as a concrete class inheriting from
StatusBase<Status, PW_STATUS_OK>.
Each StatusBase specialization designates one code value as success (ok()
== true). Custom status types are constructed from a status code (e.g.
MyStatus(MyStatus::Code::kFailed)). They default construct to the OK code.
The underlying code type is aliased as MyStatus::Code.
Enum status codes#
Enums are recommended for custom status codes because they prevent arbitrary or
unknown values and allow the compiler to warn about unhandled cases in
switch statements.
The recommended way to define an enum-based status type is with
pw_enum’s PW_ENUM_STATUS macro (see
Defining custom status types). PW_ENUM_STATUS generates a
StatusBase-derived class from an enum, complete with a factory function for
each error code, Is*() query functions, stringification, and tokenized
logging support.
If not using pw_enum, pw::StatusOf (guide) is a final, [[nodiscard]]
status class template that wraps an existing enum:
enum class DriverError : uint8_t {
kOk,
kBusFault,
kTimeout,
};
using DriverStatus = pw::StatusOf<DriverError::kOk>;
constexpr DriverStatus WriteByte(uint8_t byte) {
if (byte == 0xFF) {
return DriverStatus(DriverError::kBusFault);
}
return DriverStatus();
}
// Propagate custom errors using PW_TRY.
constexpr DriverStatus SendPacket(uint8_t header, uint8_t payload) {
PW_TRY(WriteByte(header));
PW_TRY(WriteByte(payload));
return DriverStatus();
}
Callers inspect the status or compare it directly against the underlying enum:
DriverStatus status = SendPacket(0x01, 0x02);
if (status.ok()) {
// Packet sent successfully.
} else if (status == DriverError::kBusFault) {
// Handle bus fault.
}
To fully customize the status class, subclass StatusBase directly.
Non-enum status codes#
Enums are recommended for status codes because they prevent arbitrary or unknown
values and allow the compiler to warn about unhandled cases in switch
statements. However, StatusBase also supports non-enum types such as
integers when interfacing with existing protocols or numeric error codes. For
example, HTTP status codes can be represented with 200 designating success:
// StatusBase can also be used with non-enum types such as integers.
class [[nodiscard]] HttpStatus final : public pw::StatusBase<HttpStatus, 200> {
public:
constexpr HttpStatus() = default;
explicit constexpr HttpStatus(int code) : StatusBase(code) {}
};
constexpr HttpStatus FetchResource(bool resource_found) {
if (!resource_found) {
return HttpStatus(404); // Explicit construction for non-OK codes
}
return HttpStatus(); // Defaults to 200 OK
}
Callers handle non-enum statuses in the same way:
HttpStatus status = FetchResource(true);
if (status.ok()) {
// Request succeeded (200).
} else {
// Handle HTTP error code (e.g. status.code() == 404).
}
Should I use a custom status?#
Custom status types offer significant advantages, but are not a fit for every scenario or project.
Advantages
Resolve ambiguity: Each distinct error can map to its own code. No more wondering which resource was exhausted for
RESOURCE_EXHAUSTED.Better API contracts for callers: APIs only produce codes that make sense. Callers do not have consider codes that will never be returned.
Better API contracts for implementers: API implementers are constrained to defined codes and cannot return codes that do not apply. No need to check that an implementation returned a supported code.
Thoughtful layering of status domains: Prevent users from mindlessly forwarding errors between API layers with PW_TRY, which loses meaning and specificity.
Disadvantages
Cognitive load: Users have to learn different status codes for different APIs.
Enum proliferation: Custom statuses could lead to a proliferation of enums, even when fewer status types would be better.
Whether and how to use custom status types is ultimately a project decision.