JSON Voorhees
Killer JSON for C++
Loading...
Searching...
No Matches
optional.hpp
Go to the documentation of this file.
1/// \file jsonv/optional.hpp
2/// Pulls in an implementation of \c optional.
3///
4/// Copyright (c) 2016-2026 by Travis Gockel. All rights reserved.
5///
6/// This program is free software: you can redistribute it and/or modify it under the terms of the Apache License
7/// as published by the Apache Software Foundation, either version 2 of the License, or (at your option) any later
8/// version.
9///
10/// \author Travis Gockel (travis@gockelhut.com)
11#pragma once
12
13#include <jsonv/config.hpp>
14
15#include <memory>
16#include <optional>
17#include <type_traits>
18#include <utility>
19
20namespace jsonv
21{
22
23namespace detail
24{
25
26/// Does binding a \c T& to a \c U, as direct-initialization does, refer to an object which is already there -- an
27/// lvalue, or the one something converts to, as a \c std::reference_wrapper does -- rather than to a temporary made for
28/// the binding?
29///
30/// This is <tt>std::is_constructible_v<T&, U> && !std::reference_constructs_from_temporary_v<T&, U></tt>, for the \c U
31/// a forwarding reference deduces, which is what \c std::optional<T&> asks. That trait is C++23 and not every standard
32/// library has it, so where it is missing the answer is worked out from what a reference can bind to:
33///
34/// - A reference to non-\c const binds to nothing a temporary could be made for, so whatever it binds to is there.
35/// - An lvalue of \c T, or of a class derived from it, is bound to directly.
36/// - Any other class reaches a \c T& by converting. A conversion which makes a new object makes something a
37/// <tt>T&&</tt> binds to as well, and one which yields an lvalue, explicitly or not, does not.
38///
39/// That never takes a binding the trait would refuse, but it refuses a few the trait would take: a conversion yielding
40/// an xvalue, and -- since GCC lets a class's own copy constructor make a <tt>T&&</tt> out of what a conversion yields
41/// -- a \c std::reference_wrapper to a class with a user-declared copy constructor. Neither arises where the trait is
42/// there, which is every toolchain this library is built with in CI except, possibly, MSVC's and Apple's.
43template <typename T, typename U>
44concept binds_without_temporary =
45 std::is_constructible_v<T&, U>
46#if defined(__cpp_lib_reference_from_temporary) && __cpp_lib_reference_from_temporary >= 202202L
47 && !std::reference_constructs_from_temporary_v<T&, U>;
48#else
49 && ( !std::is_const_v<T>
50 || (std::is_lvalue_reference_v<U> && std::is_convertible_v<std::remove_reference_t<U>*, T*>)
51 || ( std::is_class_v<std::remove_cvref_t<U>>
52 && !std::is_convertible_v<std::remove_reference_t<U>*, T*>
53 && !std::is_constructible_v<T&&, U>
54 )
55 );
56#endif
57
58/// What \c jsonv::optional is for a reference: \c std::optional<T&> as C++26 specifies it (P2988, with P3836), for a
59/// library which cannot require C++26 yet. \a T is the type referred to, and may be \c const.
60///
61/// It holds a pointer and nothing else -- an empty one is a null pointer -- so it costs what the pointer it stands in
62/// for would, and it is trivially copyable. Assigning to one rebinds it rather than assigning through to the object it
63/// refers to. It binds to anything a \c T& binds to without making a temporary, a \c std::reference_wrapper among
64/// them, and refuses whatever would leave it referring to a temporary, which would be gone by the end of the
65/// statement. All of that is what \c std::optional<T&> does.
66///
67/// It has the constructors, observers and modifiers of \c std::optional<T&>, and compares with \c std::nullopt. It
68/// does not have the iterator or monadic members, which nothing here uses. Add them to the standard's specification
69/// before reaching for them, so that replacing this with \c std::optional changes nothing.
70template <typename T>
71class optional_ref final
72{
73public:
74 using value_type = T;
75
76public:
77 /// An empty instance.
78 constexpr optional_ref() noexcept = default;
79
80 /// An empty instance.
81 constexpr optional_ref(std::nullopt_t) noexcept
82 { }
83
84 /// Refer to what \a ref is, or converts to -- implicitly, where a \c U converts to a \c T& implicitly.
85 template <typename U>
86 requires (!std::is_same_v<std::remove_cvref_t<U>, optional_ref> && binds_without_temporary<T, U>)
87 constexpr explicit(!std::is_convertible_v<U, T&>) optional_ref(U&& ref)
88 noexcept(std::is_nothrow_constructible_v<T&, U>) :
89 _ptr(std::addressof(static_cast<T&>(std::forward<U>(ref))))
90 { }
91
92 /// Binding to a temporary would leave this referring to an object which no longer exists.
93 template <typename U>
94 requires ( !std::is_same_v<std::remove_cvref_t<U>, optional_ref>
95 && !binds_without_temporary<T, U>
96 && std::is_constructible_v<T&, U>
97 )
98 optional_ref(U&&) = delete;
99
100 /// Refer to whatever \a other refers to, where a \c U& converts to a \c T& -- as a \c value& does to a
101 /// <tt>const value&</tt>.
102 template <typename U>
103 requires (!std::is_same_v<U, T> && std::is_convertible_v<U*, T*>)
104 constexpr optional_ref(const optional_ref<U>& other) noexcept :
105 _ptr(other._ptr)
106 { }
107
108 /// Make this empty.
109 constexpr optional_ref& operator=(std::nullopt_t) noexcept
110 {
111 _ptr = nullptr;
112 return *this;
113 }
114
115 /// Does this refer to anything?
117 constexpr bool has_value() const noexcept
118 {
119 return _ptr != nullptr;
120 }
121
122 /// See \ref has_value.
124 constexpr explicit operator bool() const noexcept
125 {
126 return has_value();
127 }
128
129 /// \{
130
131 /// Get the object referred to. Like \c std::optional, this does not check: on an empty instance the behavior is
132 /// undefined. \ref value is the checked form.
134 constexpr T& operator*() const noexcept
135 {
136 return *_ptr;
137 }
138
140 constexpr T* operator->() const noexcept
141 {
142 return _ptr;
143 }
144 /// \}
145
146 /// Get the object referred to.
147 ///
148 /// \throws std::bad_optional_access if this is empty.
150 constexpr T& value() const
151 {
152 if (!_ptr)
153 throw std::bad_optional_access();
154
155 return *_ptr;
156 }
157
158 /// Get a copy of the object referred to, or \a fallback if this is empty. A copy, because \a fallback is
159 /// usually a temporary and a reference to it would dangle.
160 template <typename U = std::remove_cv_t<T>>
162 constexpr std::remove_cv_t<T> value_or(U&& fallback) const
163 {
164 if (_ptr)
165 return *_ptr;
166 else
167 return static_cast<std::remove_cv_t<T>>(std::forward<U>(fallback));
168 }
169
170 /// Refer to what \a ref is, or converts to, instead of whatever this referred to before, and return it.
171 template <typename U>
172 requires binds_without_temporary<T, U>
173 constexpr T& emplace(U&& ref) noexcept(std::is_nothrow_constructible_v<T&, U>)
174 {
175 T& target = static_cast<T&>(std::forward<U>(ref));
176 _ptr = std::addressof(target);
177 return target;
178 }
179
180 /// Binding to a temporary would leave this referring to an object which no longer exists.
181 template <typename U>
182 requires (!binds_without_temporary<T, U> && std::is_constructible_v<T&, U>)
183 T& emplace(U&&) = delete;
184
185 /// Make this empty.
186 constexpr void reset() noexcept
187 {
188 _ptr = nullptr;
189 }
190
191 constexpr void swap(optional_ref& other) noexcept
192 {
193 std::swap(_ptr, other._ptr);
194 }
195
196 friend constexpr void swap(optional_ref& a, optional_ref& b) noexcept
197 {
198 a.swap(b);
199 }
200
202 friend constexpr bool operator==(const optional_ref& self, std::nullopt_t) noexcept
203 {
204 return !self._ptr;
205 }
206
207private:
208 template <typename U>
209 friend class optional_ref;
210
211private:
212 T* _ptr = nullptr;
213};
214
215template <typename T>
216struct optional_type
217{
218 using type = std::optional<T>;
219};
220
221template <typename T>
222struct optional_type<T&>
223{
224 using type = optional_ref<T>;
225};
226
227}
228
229/// Represents a value that may or may not be present.
230///
231/// This is \c std::optional<T> for every \a T it accepts. A reference is the exception: \c std::optional of one is
232/// ill-formed until C++26 (P2988), and the standard libraries of MSVC and Apple's Clang do not have it yet. Until every
233/// toolchain this library supports does, <tt>optional<T&></tt> is \c detail::optional_ref, which behaves as
234/// \c std::optional<T&> does as far as it goes. Once they all do, this is \c std::optional throughout.
235///
236/// It is chosen by the reference rather than by which standard library is present, so that the library and a program
237/// built against it in another language mode agree on what every signature means.
238template <typename T>
239using optional = typename detail::optional_type<T>::type;
240
241}
Copyright (c) 2014-2020 by Travis Gockel.
#define JSONV_NODISCARD
Warn if the caller discards the result of this function.
Definition config.hpp:143
JSONV_PUBLIC void swap(value &a, value &b) noexcept
Swap the values a and b.
STL namespace.
typename detail::optional_type< T >::type optional
Represents a value that may or may not be present.
Definition optional.hpp:239