blob: 8ab24959ce8e4bbea0363ade78a5baa46e5fed16 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_TYPES_OPTIONAL_UTIL_H_
#define BASE_TYPES_OPTIONAL_UTIL_H_
#include <concepts>
#include <functional>
#include <optional>
#include <utility>
#include "base/types/expected.h"
namespace base {
// Helper for converting an `std::optional<T>` to a pointer suitable for
// passing as a function argument (alternatively, consider using
// `base::optional_ref`):
//
// void MaybeProcessData(const std::string* optional_data);
//
// class Example {
// public:
// void DoSomething() {
// MaybeProcessData(base::OptionalToPtr(data_));
// }
//
// private:
// std::optional<std::string> data_;
// };
//
// Rationale: per the C++ style guide, if `T` would normally be passed by
// reference, the optional version should be passed as `T*`, and *not* as
// `const std::optional<T>&`. Passing as `const std::optional<T>&` leads to
// implicit constructions and copies, e.g.:
//
// // BAD: a caller passing a `std::string` implicitly copies the entire string
// // to construct a temporary `std::optional<std::string>` to use for the
// // function argument.
// void BadMaybeProcessData(const std::optional<std::string>& optional_data);
//
// For more background, see https://abseil.io/tips/163. Also see
// `base/types/optional_ref.h` for an alternative approach to
// `const std::optional<T>&` that does not require the use of raw pointers.
template <class T>
const T* OptionalToPtr(const std::optional<T>& optional) {
return optional.has_value() ? &optional.value() : nullptr;
}
template <class T>
T* OptionalToPtr(std::optional<T>& optional) {
return optional.has_value() ? &optional.value() : nullptr;
}
// Helper for creating an `std::optional<T>` from a `T*` which may be null.
//
// This copies `T` into the `std::optional`. When you have control over the
// function that accepts the optional, and it currently expects a
// `std::optional<T>&` or `const std::optional<T>&`, consider changing it to
// accept a `base::optional_ref<T>` / `base::optional_ref<const T>` instead,
// which can be constructed from `T*` without copying.
template <class T>
std::optional<T> OptionalFromPtr(const T* value) {
return value ? std::optional<T>(*value) : std::nullopt;
}
// Helper for creating a `base::expected<U, F>` from an `std::optional<T>` and
// an error of type E, where T is convertible to U and E is convertible to F. If
// `opt` contains a value, this copies it into the `base::expected`, otherwise
// it moves `err` in.
template <class T, class E, class U = T, class F = E>
base::expected<U, F> OptionalToExpected(const std::optional<T>& opt, E&& err)
requires(std::convertible_to<T, U> && std::copyable<T> &&
std::convertible_to<E, F> && std::movable<E>)
{
if (opt.has_value()) {
return base::ok(opt.value());
}
return base::unexpected(std::move(err));
}
// As above, but copies `err` into the `base:expected` if `opt` doesn't contain
// a value.
template <class T, class E, class U = T, class F = E>
base::expected<U, F> OptionalToExpected(const std::optional<T>& opt,
const E& err)
requires(std::convertible_to<T, U> && std::copyable<T> &&
std::convertible_to<E, F> && std::copyable<E>)
{
if (opt.has_value()) {
return base::ok(opt.value());
}
return base::unexpected(err);
}
// Helper for creating an `std::optional<U>` from a `base::expected<T, E>`,
// where T is convertible to U. If `exp` contains a value, this copies it into
// the `std::optional`, otherwise it returns std::nullopt.
template <class T, class E, class U = T>
std::optional<U> OptionalFromExpected(const base::expected<T, E>& exp)
requires(std::convertible_to<T, U>)
{
if (exp.has_value()) {
return std::optional(exp.value());
}
return std::nullopt;
}
// Unwrap a `std::optional<T>` if it holds a value, and set `out` to the value
// in the optional. Returns true if the optional held a value which means `out`
// was assigned to. Returns false if the optional was empty, in which casse
// `out` will be unchanged (and the `proj` function will not be called).
//
// If a `proj` function is provided, it can modify the value in the optional,
// and `out` will instead be set to the value returned from the `proj` function.
// The `proj` may return any value or type, as long as its type matches or is
// assignable to `out`.
//
// If the optional is moved to this function as an rvalue, the unwrapped value
// will also be moved to assign to `out` as an rvalue (or moved into the `proj`
// function argument).
//
// # Examples
// Simple usage that unwraps the inner value into a variable or early outs:
// ```
// void maybe_do_stuff(std::optional<int> o) {
// int val = 0;
// if (!OptionalUnwrapTo(o, val)) {
// return;
// }
// do_stuff(val);
// }
// ```
//
// Unwraps the inner value and converts it to a different type:
// ```
// void maybe_do_stuff(std::optional<int> o) {
// MyType val;
// if (!OptionalUnwrapTo(o, val, [](int i) { return MyType::FromId(i); })) {
// return;
// }
// do_stuff(val);
// }
// ```
template <class T, class O, class P = std::identity>
requires(std::invocable<P, const T&>) &&
requires(O& out, std::invoke_result_t<P, const T&> val) { out = val; }
bool OptionalUnwrapTo(const std::optional<T>& optional, O& out, P proj = {}) {
if (optional) {
// Note: `proj` is received by value not by ref (unlike similar std
// functions) as we see a large binary size impact from the latter. So we
// unconditionally move it here while invoking.
out = std::invoke(std::move(proj), optional.value());
return true;
}
return false;
}
template <class T, class O, class P = std::identity>
requires(std::invocable<P, T &&>) &&
requires(O& out, std::invoke_result_t<P, T&&> val) { out = val; }
bool OptionalUnwrapTo(std::optional<T>&& optional, O& out, P proj = {}) {
if (optional) {
// Note: `proj` is received by value not by ref (unlike similar std
// functions) as we see a large binary size impact from the latter. So we
// unconditionally move it here while invoking.
out = std::invoke(std::move(proj), std::move(optional).value());
return true;
}
return false;
}
} // namespace base
#endif // BASE_TYPES_OPTIONAL_UTIL_H_