blob: 86babf35b0aa72c40692803c736d51804edce7ee [file] [view]
# TypedStatus<T>
The purpose of TypedStatus is to provide a thin wrapper around return-value
enums that support causality tracking, data attachment, and general assistance
with debugging, without adding slowdowns due to returning large structs,
pointers, or more complicated types.
TypedStatus<T> should be specialized with a traits struct that defines:
Codes - enum (usually enum class) that would be the return type, if we weren't
using TypedStatus.
static constexpr StatusGroupType Group() { return "NameOfStatus"; }
static constexpr absl::optional<Codes> DefaultEnumValue() {
return Codes::kCodeThatShouldBeSuperOptimizedEGSuccess;
// Can return nullopt to optimize none of them. No idea why you'd do that.
}
Typically one would:
struct MyStatusTraits { ... };
using MyStatus = TypedStatus<MyStatusTraits>;
## Using an existing `TypedStatus<T>`
The current canonical TypedStatus is called `Status` for historical reasons,
though that will soon change.
All TypedStatus specializations have the following common API:
```c++
// The underlying code value.
T::Codes code() const;
// The underlying message.
std::string& message() const;
// Adds the current file & line number to the trace.
TypedStatus<T>&& AddHere() &&;
// Adds some named data to the status, such as a platform
// specific error value, ie: HRESULT. This data is for human consumption only
// in a developer setting, and can't be extracted from the TypedStatus
// normally. The code value should be sufficiently informative between sender
// and reciever of the TypedStatus.
template<typename D>
TypedStatus<T>&& WithData(const char *key, const D& value) &&;
template<typename D>
void WithData(const char *key, const D& value) &;
// Adds a "causal" status to this one.
// The type `R` will not be retained, and similarly with the data methods,
// `cause` will only be used for human consumption, and cannot be extracted
// under normal circumstances.
template<typename R>
TypedStatus<T>&& AddCause(TypedStatus<R>&& cause) &&;
template<typename R>
void AddCause(TypedStatus<R>&& cause) &;
```
## Quick usage guide
If you have an existing enum, and would like to wrap it:
```c++
enum class MyExampleEnum : StatusCodeType {
kDefaultValue = 1,
kThisIsAnExample = 2,
kDontArgueInTheCommentSection = 3,
};
```
Define an |TypedStatusTraits|, picking a name for the group of codes:
(copying the desciptive comments is not suggested)
```c++
struct MyExampleStatusTraits {
// If you do not have an existing enum, you can `enum class Codes { ... };`
// here, instead of `using`.
using Codes = MyExampleEnum;
static constexpr StatusGroupType Group() { return "MyExampleStatus"; }
static constexpr absl::optional<Codes> { return Codes::kDefaultValue; }
}
```
Bind your typename:
```c++
using MyExampleStatus = media::TypedStatus<MyExampleStatusTraits>;
```
Use your new type:
```c++
MyExampleStatus Foo() {
return MyExampleStatus::Codes::kThisIsAnExample;
}
int main() {
auto result = Foo();
switch(result.code()) {
case MyExampleStatus::Codes::...:
break;
...
}
}
```
For the common case where you'd like to return some constructed thing OR
an error type, we've also created `TypedStatus<T>::Or<D>`.
The `TypedStatus<T>::Or<D>` type can be constructed implicitly with either
a `TypedStatus<T>`, a `T`, or a `D`.
This type has methods:
```c++
bool has_value() const;
bool has_error() const;
// Return the error, if we have one.
// Callers should ensure that this `has_error()`.
TypedStatus<T> error() &&;
// Return the value, if we have one.
// Callers should ensure that this `has_value()`.
OtherType value() &&;
// It is invalid to call `code()` on an `Or<D>` type when
// has_value() is true and TypedStatusTraits<T>::DefaultEnumValue is nullopt.
T::Codes code();
```
Example usage:
```c++
MyExampleStatus::Or<std::unique_ptr<VideoDecoder>> CreateAndInitializeDecoder() {
std::unique_ptr<VideoDecoder> decoder = decoder_factory_->GiveMeYourBestDecoder();
auto init_status = decoder->Initialize(init_args_);
// If the decoder initialized successfully, then just return it.
if (init_status == InitStatusCodes::kOk)
return std::move(decoder);
// Otherwise, return a MediaExampleStatus caused by the init status.
return MyExampleStatus(MyExampleEnum::kDontArgueInTheCommentSection).AddCause(
std::move(init_status));
}
int main() {
auto result = CreateAndInitializeDecoder();
if (result.has_value())
decoder_loop_->SetDecoder(std::move(result).value());
else
logger_->SendError(std::move(result).error());
}
```
## Additional setup for mojo
If you want to send a specialization of TypedStatus over mojo,
add the following to media_types.mojom:
```
struct MyExampleEnum {
StatusBase? internal;
};
```
And add the following to media/mojo/mojom/BUILD.gn near the `StatusData` type
binding.
```
{
mojom = "media.mojom.MyExampleEnum",
cpp = "::media::MyExampleEnum"
},
```
## Design decisions
See go/typedstatus for design decisions.