From 550063a199392d88d801700d4ec77163f1caff87 Mon Sep 17 00:00:00 2001 From: Mark Rowe Date: Wed, 5 Aug 2026 00:45:01 -0700 Subject: [PATCH] Add bn::base::expected, a backport of C++23's std::expected `bn::base::expected` provides a way to represent either of two values: an expected value of type `T` or an unexpected value of type `E`. This is commonly used as the return type of a function that can fail, where `T` will hold the return value on success and `E` will hold the error on failure. Most functionality of `std::expected` is implemented. The header lists the aspects that were skipped for now. --- base/expected.h | 553 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 553 insertions(+) create mode 100644 base/expected.h diff --git a/base/expected.h b/base/expected.h new file mode 100644 index 000000000..cc52a96f1 --- /dev/null +++ b/base/expected.h @@ -0,0 +1,553 @@ +// Copyright (c) 2026 Vector 35 Inc +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +#pragma once + +// A value of type T or an error of type E, backporting C++23's std::expected. A function that can +// fail returns one of these instead of pairing an optional with an out-parameter. +// +// The special member functions are trivial whenever T and E make them trivial, so an expected of +// trivially copyable types is itself trivially copyable. This uses conditionally trivial special +// member functions (P0848), which requires GCC 10, Clang 16 or MSVC 19.28. +// +// Differences from std::expected, all of which can be filled in when something needs them: +// - No in-place construction: no in_place_t / unexpect_t constructors, and no emplace(). +// - No conversion between different specializations, so an expected does not convert to an +// expected even when U converts to T and G converts to E. +// - No monadic operations: and_then(), or_else(), transform() and transform_error(). +// - No swap(). + +#include +#include +#include // IWYU pragma: keep +#include +#include + +namespace bn::base { + +namespace detail { + +// Replace the active member of a union. Destroy oldMember and construct newMember from args. If +// constructing throws, oldMember is restored, so the union is left holding what it held before. +template +void ReplaceUnionMember(New* newMember, Old* oldMember, Args&&... args) +{ + if constexpr (std::is_nothrow_constructible_v) + { + oldMember->~Old(); + ::new (static_cast(newMember)) New(std::forward(args)...); + } + else if constexpr (std::is_nothrow_move_constructible_v) + { + // Construct before destroying anything, so a throw here leaves the union untouched + New created(std::forward(args)...); + oldMember->~Old(); + ::new (static_cast(newMember)) New(std::move(created)); + } + else + { + static_assert(std::is_nothrow_move_constructible_v, + "assigning across the value and error types of an expected requires one of them to be nothrow " + "move constructible"); + + Old saved(std::move(*oldMember)); + oldMember->~Old(); + try + { + ::new (static_cast(newMember)) New(std::forward(args)...); + } + catch (...) + { + ::new (static_cast(oldMember)) Old(std::move(saved)); + throw; + } + } +} + + +// Whether Type is a specialization of Template +template class Template> +constexpr bool IsSpecializationOf = false; + +template