|
| 1 | +/** |
| 2 | +@page common-error-handling Error Handling |
| 3 | + |
| 4 | +[StatusOr<T>]: @ref google::cloud::StatusOr |
| 5 | +[StatusOr<T>::value()]: @ref google::cloud::StatusOr::value() |
| 6 | +[StreamRange<S>]: @ref google::cloud::StreamRange |
| 7 | +[AsyncStreamingReadWriteRpc<T,U>]: @ref google::cloud::AsyncStreamingReadWriteRpc |
| 8 | +[future<T>]: @ref google::cloud::future |
| 9 | +[RuntimeStatusError]: @ref google::cloud::RuntimeStatusError |
| 10 | +[error_info()]: @ref google::cloud::Status::error_info() |
| 11 | +[input iterators]: https://en.cppreference.com/w/cpp/named_req/InputIterator |
| 12 | +[iostream operator<<]: @ref google::cloud::operator<<(std::ostream&,google::cloud::Status const&) |
| 13 | + |
| 14 | +@par Overview |
| 15 | + |
| 16 | +In general, the `google-cloud-cpp` libraries return a [StatusOr<T>] if a |
| 17 | +function may fail and needs to signal an error. `StatusOr<T>` is an "outcome", |
| 18 | +it contains either the value returned on success, or a description of the |
| 19 | +error. Errors are represented by @ref google::cloud::Status, thus the name. |
| 20 | +If you are familiar with `std::expected` from C++23, `StatusOr<T>` plays a |
| 21 | +similar role, but does not attempt to be compatible with it. |
| 22 | + |
| 23 | +If you are planning to log a `Status`, consider using the [iostream operator<<]. |
| 24 | +A `Status` contains more than just the message, in particular, its |
| 25 | +[error_info()] member function may return additional information that is useful |
| 26 | +during troubleshooting. |
| 27 | + |
| 28 | +@par Stream Ranges |
| 29 | + |
| 30 | +Some functions return [StreamRange<S>], where `S` is a `StatusOr<T>`. These |
| 31 | +ranges provide [input iterators] that paginate or stream results from a service, |
| 32 | +offering a more idiomatic API. The value type in these iterators is |
| 33 | +`StatusOr<T>` because the stream may fail after it has successfully returned |
| 34 | +some values. For example, if the request to obtain the next page of results |
| 35 | +fails, or if the underlying stream is interrupted by the service. |
| 36 | + |
| 37 | +@par Futures |
| 38 | + |
| 39 | +Some functions return a "future" ([future<T>]). These objects represent a value |
| 40 | +that will be obtained asynchronously. By the very nature of asynchronous |
| 41 | +operations, the request may fail after the function is called. Therefore, we |
| 42 | +have chosen to return `future<StatusOr<T>>`. We think the alternatives are |
| 43 | +either incorrect (e.g. `StatusOr<future<T>>` can only handle errors detected |
| 44 | +before the function returns), or overly complex |
| 45 | +(`StatusOr<future<StatusOr<T>>>`). |
| 46 | + |
| 47 | +@par Values with specific error handling |
| 48 | + |
| 49 | +Some functions return a value that already has a mechanism to signal failures. |
| 50 | +For example, some functions return [AsyncStreamingReadWriteRpc<T,U>] (or |
| 51 | +technically `std::unique_ptr<AsyncStreamingReadWriteRpc<T,U>>`). A small number |
| 52 | +of functions return classes derived from `std::istream` or `std::ostream. |
| 53 | +In such cases, the library does not wrap the result in a `StatusOr<T>` because |
| 54 | +the returned type already has mechanisms to signal errors. |
| 55 | + |
| 56 | +@par Example: Using StatusOr<T> |
| 57 | + |
| 58 | +You can check that a `StatusOr<T>` contains a value by calling the `.ok()` |
| 59 | +method, or by using `operator bool()` (like with other smart pointers). If |
| 60 | +there is no value, you can access the contained `Status` object using the |
| 61 | +`.status()` member. If there is a value, you may access it by dereferencing |
| 62 | +with `operator*()` or `operator->()`. As with all smart pointers, callers must |
| 63 | +first check that the `StatusOr<T>` contains a value before dereferencing and |
| 64 | +accessing the contained value. |
| 65 | + |
| 66 | +@snippet samples.cc status-or-usage |
| 67 | + |
| 68 | +@par Example: Using StatusOr<T> with exceptions |
| 69 | + |
| 70 | +Some applications prefer to throw exceptions on errors. In this case, consider |
| 71 | +using [StatusOr<T>::value()]. This function throws a [RuntimeStatusError] if |
| 72 | +there is no value, and returns the value otherwise. |
| 73 | + |
| 74 | +@note If you're compiling with exceptions disabled, calling `.value()` on a |
| 75 | +`StatusOr<T>` that does not contain a value will terminate the program |
| 76 | +instead of throwing. |
| 77 | + |
| 78 | +@snippet samples.cc status-or-exceptions |
| 79 | + |
| 80 | +@par Error Handling in google-cloud-cpp code samples |
| 81 | + |
| 82 | +The code samples for `google-cloud-cpp` try to emphasize how to use specific |
| 83 | +APIs and often have minimal error handling. A typical code sample may simply |
| 84 | +throw the status on error, like so: |
| 85 | + |
| 86 | +@code {.cpp} |
| 87 | +namespace svc = ::google::cloud::some_service; |
| 88 | +[](svc::Client client, std::string const& key) { |
| 89 | + auto response = client.SomeRpc(key); |
| 90 | + if (!response) throw std::move(response).status(); |
| 91 | + // ... example continues here ... |
| 92 | +} |
| 93 | +@endcode |
| 94 | + |
| 95 | +This should not be interpreted as a best practice. If your application is |
| 96 | +designed to work with exceptions, then using [StatusOr<T>::value()] is a better |
| 97 | +alternative. We just want to show that some error handling is required for these |
| 98 | +functions, but at the same time we don't want to obscure the example with a lot |
| 99 | +of error handling code. |
| 100 | + |
| 101 | +@see @ref google::cloud::StatusOr |
| 102 | +@see @ref google::cloud::Status the class used to describe errors. |
| 103 | +@see @ref google::cloud::future for more details on the type returned |
| 104 | + by asynchronous operations. |
| 105 | +@see https://en.cppreference.com/w/cpp/utility/expected for more information |
| 106 | + about `std::expected` |
| 107 | + |
| 108 | +*/ |
0 commit comments