// 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) { … } template <class T> T* OptionalToPtr(std::optional<T>& optional) { … } // 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) { … } // 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>) { … } // 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>) { … } // 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>) { … } // 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) { … } template <class T, class O, class P = std::identity> requires(std::invocable<P, T &&>) && requires(O& out, std::invoke_result_t<P, T&&> val) { … } } // namespace base #endif // BASE_TYPES_OPTIONAL_UTIL_H_