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 enums or codes with pw::StatusBase
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. A designated code value represents success (ok() == true).
Pigweed’s standard pw::Status is itself implemented as a concrete class
inheriting from StatusBase<Status, PW_STATUS_OK>.
Custom status types can be defined using the PW_STATUS_TYPE macro
or by directly subclassing pw::StatusBase:
Macro:
PW_STATUS_TYPE(MyStatus, MyEnum::kOk)defines a final status class with[[nodiscard]]. The status code must be an enum.Subclass: Subclassing
pw::StatusBase<MyStatus, MyEnum::kOk>directly (using the Curiously Recurring Template Pattern / CRTP) allows customizing the status class with additional domain-specific member functions. Derived classes should be declared[[nodiscard]]and define constructors forwarding toStatusBase, whose constructors are protected.
Custom status types are constructed from a status code (e.g.
MyStatus(MyStatus::Code::kFailed)). They default construct to the OK code.
The code type is available as MyStatus::Code.
Custom error enums#
Many drivers and hardware interfaces define their own error enumerations.
StatusBase allows using these enumerations while retaining status semantics,
including .ok() checks, [[nodiscard]] enforcement, and error propagation
with PW_TRY:
enum class DriverError : uint8_t {
kOk,
kBusFault,
kTimeout,
};
PW_STATUS_TYPE(DriverStatus, 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.
}
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.