Error Handling
Overview
In general, the google-cloud-cpp
libraries return a StatusOr if a function may fail and needs to signal an error. StatusOr<T>
is an "outcome", it contains either the value returned on success, or a description of the error. Errors are represented by Status, thus the name. If you are familiar with std::expected
from C++23, StatusOr<T>
plays a similar role, but does not attempt to be compatible with it.
If you are planning to log a Status
, consider using the iostream operator<<. A Status
contains more than just the message, in particular, its error_info() member function may return additional information that is useful during troubleshooting.
Stream Ranges
Some functions return StreamRange, where S
is a StatusOr<T>
. These ranges provide input iterators that paginate or stream results from a service, offering a more idiomatic API. The value type in these iterators is StatusOr<T>
because the stream may fail after it has successfully returned some values. For example, if the request to obtain the next page of results fails, or if the underlying stream is interrupted by the service.
Futures
Some functions return a "future" (future). These objects represent a value that will be obtained asynchronously. By the very nature of asynchronous operations, the request may fail after the function is called. Therefore, we have chosen to return future<StatusOr<T>>
. We think the alternatives are either incorrect (e.g. StatusOr<future<T>>
can only handle errors detected before the function returns), or overly complex (StatusOr<future<StatusOr<T>>>
).
Values with specific error handling
Some functions return a value that already has a mechanism to signal failures. For example:
- Some functions return AsyncStreamingReadWriteRpc<T,U>. Or to be technical, they return
std::unique_ptr<AsyncStreamingReadWriteRpc<T,U>>
. - A small number of functions return classes derived from
std::istream
orstd::ostream
.
In such cases, the library does not wrap the result in a StatusOr<T>
because the returned type already has mechanisms to signal errors.
Example: Using StatusOr
You can check that a StatusOr<T>
contains a value by calling the .ok()
method, or by using operator bool()
(like with other smart pointers). If there is no value, you can access the contained Status
object using the .status()
member. If there is a value, you may access it by dereferencing with operator*()
or operator->()
. As with all smart pointers, callers must first check that the StatusOr<T>
contains a value before dereferencing and accessing the contained value.
namespace gc = ::google::cloud;
[](std::string const& project_name) {
gc::StatusOr<gc::Project> project = gc::MakeProject(project_name);
if (!project) {
std::cerr << "Error parsing project <" << project_name
<< ">: " << project.status() << "\n";
return;
}
std::cout << "The project id is " << project->project_id() << "\n";
}
Example: Using StatusOr
Some applications prefer to throw exceptions on errors. In this case, consider using StatusOr<T>::value()
. This function throws a RuntimeStatusError if there is no value, and returns the value otherwise.
namespace gc = ::google::cloud;
[](std::string const& project_name) {
try {
gc::Project project = gc::MakeProject(project_name).value();
std::cout << "The project id is " << project.project_id() << "\n";