Get started & guides#
pw_status: Exception-free error propagation for embedded
Get started#
To deploy pw_status
, depend on the library:
Add @pigweed//pw_status
to the deps
list in your Bazel target:
cc_library("...") {
# ...
deps = [
# ...
"@pigweed//pw_status",
# ...
]
}
This assumes that your Bazel WORKSPACE
has a repository named @pigweed
that points to the upstream Pigweed repository.
Add $dir_pw_status
to the deps
list in your pw_executable()
build target:
pw_executable("...") {
# ...
deps = [
# ...
"$dir_pw_status",
# ...
]
}
Add pw_status
to your pw_add_library
or similar CMake target:
pw_add_library(my_library STATIC
HEADERS
...
PRIVATE_DEPS
# ...
pw_status
# ...
)
There are two ways to use pw_status
from a Zephyr project:
Depend on
pw_status
in your CMake target (see CMake tab). This is the Pigweed team’s suggested approach since it enables precise CMake dependency analysis.Add
CONFIG_PIGWEED_STATUS=y
to the Zephyr project’s configuration, which causespw_status
to become a global dependency and have the includes exposed to all targets. The Pigweed team does not recommend this approach, though it is the typical Zephyr solution.
Then use the status object or try macros:
#include "pw_status/try.h"
#include "pw_status/status.h"
pw::Status MyOperation() {
PW_TRY(SubOp1());
PW_TRY(SubOp2());
// ...
return pw::OkStatus();
}
Guides#
Tracking the first error encountered#
In some contexts it is useful to track the first error encountered while
allowing execution to continue. Manually writing out if
statements to check
and then assign quickly becomes verbose, and doesn’t explicitly highlight the
intended behavior of “latching” to the first error.
No: Track status manually across calls
Status overall_status;
for (Sector& sector : sectors) {
Status erase_status = sector.Erase();
if (!overall_status.ok()) {
overall_status = erase_status;
}
if (erase_status.ok()) {
Status header_write_status = sector.WriteHeader();
if (!overall_status.ok()) {
overall_status = header_write_status;
}
}
}
return overall_status;
pw::Status
has a pw::Status::Update()
helper function
that does exactly this to reduce visual clutter and succinctly highlight the
intended behavior.
Yes: Track status with pw::Status::Update()
Status overall_status;
for (Sector& sector : sectors) {
Status erase_status = sector.Erase();
overall_status.Update(erase_status);
if (erase_status.ok()) {
overall_status.Update(sector.WriteHeader());
}
}
return overall_status;
Jointly reporting status with size#
pw::StatusWithSize
(pw_status/status_with_size.h
) is a convenient,
efficient class for reporting a status along with an unsigned integer value.
It is similar to the pw::Result<T>
class, but it stores both a size and a
status, regardless of the status value, and only supports a limited range (27
bits).
pw::StatusWithSize
values may be created with functions similar to
pw::Status
. For example:
#include "pw_status/status_with_size.h"
// An OK StatusWithSize with a size of 123.
StatusWithSize(123)
// A NOT_FOUND StatusWithSize with a size of 0.
StatusWithSize::NotFound()
// A RESOURCE_EXHAUSTED StatusWithSize with a size of 10.
StatusWithSize::ResourceExhausted(10)
pw::StatusWithSize
is useful for cases where an operation may partially
complete - for example read operations may read some number of bytes into an
output buffer, but not all.
Reducing error handling boilerplate#
Manual error handling through return codes is easy to understand and
straightforward to write, but leads to verbose code. To reduce boilerplate,
Pigweed has the PW_TRY
(pw_status/try.h
) macro, easing development of
functions checking or returning pw::Status
and pw::StatusWithSize
objects. The PW_TRY
and PW_TRY_WITH_SIZE
macros call a function and do
an early return if the function’s return status is not OK
.
Example:
Status PwTryExample() {
PW_TRY(FunctionThatReturnsStatus());
PW_TRY(FunctionThatReturnsStatusWithSize());
// Do something, only executed if both functions above return OK.
}
StatusWithSize PwTryWithSizeExample() {
PW_TRY_WITH_SIZE(FunctionThatReturnsStatus());
PW_TRY_WITH_SIZE(FunctionThatReturnsStatusWithSize());
// Do something, only executed if both functions above return OK.
}
PW_TRY_ASSIGN
is for working with pw::StatusWithSize
objects in in
functions that return Status. It is similar to PW_TRY
with the addition of
assigning the size from the pw::StatusWithSize
on ok.
Status PwTryAssignExample() {
size_t size_value
PW_TRY_ASSIGN(size_value, FunctionThatReturnsStatusWithSize());
// Do something that uses size_value. size_value is only assigned and this
// following code executed if the PW_TRY_ASSIGN function above returns OK.
}