Use an array of pointers for the UHJ encoder input
[openal-soft.git] / common / aloptional.h
blob6de16799e53c7f6a75b5de4e62be82bb4d1c8657
1 #ifndef AL_OPTIONAL_H
2 #define AL_OPTIONAL_H
4 #include <initializer_list>
5 #include <type_traits>
6 #include <utility>
8 #include "almalloc.h"
10 namespace al {
12 struct nullopt_t { };
13 struct in_place_t { };
15 constexpr nullopt_t nullopt{};
16 constexpr in_place_t in_place{};
18 #define NOEXCEPT_AS(...) noexcept(noexcept(__VA_ARGS__))
20 namespace detail_ {
21 /* Base storage struct for an optional. Defines a trivial destructor, for types
22 * that can be trivially destructed.
24 template<typename T, bool = std::is_trivially_destructible<T>::value>
25 struct optstore_base {
26 bool mHasValue{false};
27 union {
28 char mDummy{};
29 T mValue;
32 constexpr optstore_base() noexcept { }
33 template<typename ...Args>
34 constexpr explicit optstore_base(in_place_t, Args&& ...args)
35 noexcept(std::is_nothrow_constructible<T, Args...>::value)
36 : mHasValue{true}, mValue{std::forward<Args>(args)...}
37 { }
38 ~optstore_base() = default;
41 /* Specialization needing a non-trivial destructor. */
42 template<typename T>
43 struct optstore_base<T, false> {
44 bool mHasValue{false};
45 union {
46 char mDummy{};
47 T mValue;
50 constexpr optstore_base() noexcept { }
51 template<typename ...Args>
52 constexpr explicit optstore_base(in_place_t, Args&& ...args)
53 noexcept(std::is_nothrow_constructible<T, Args...>::value)
54 : mHasValue{true}, mValue{std::forward<Args>(args)...}
55 { }
56 ~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
59 /* Next level of storage, which defines helpers to construct and destruct the
60 * stored object.
62 template<typename T>
63 struct optstore_helper : public optstore_base<T> {
64 using optstore_base<T>::optstore_base;
66 template<typename... Args>
67 constexpr void construct(Args&& ...args) noexcept(std::is_nothrow_constructible<T, Args...>::value)
69 al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
70 this->mHasValue = true;
73 constexpr void reset() noexcept
75 if(this->mHasValue)
76 al::destroy_at(std::addressof(this->mValue));
77 this->mHasValue = false;
80 constexpr void assign(const optstore_helper &rhs)
81 noexcept(std::is_nothrow_copy_constructible<T>::value
82 && std::is_nothrow_copy_assignable<T>::value)
84 if(!rhs.mHasValue)
85 this->reset();
86 else if(this->mHasValue)
87 this->mValue = rhs.mValue;
88 else
89 this->construct(rhs.mValue);
92 constexpr void assign(optstore_helper&& rhs)
93 noexcept(std::is_nothrow_move_constructible<T>::value
94 && std::is_nothrow_move_assignable<T>::value)
96 if(!rhs.mHasValue)
97 this->reset();
98 else if(this->mHasValue)
99 this->mValue = std::move(rhs.mValue);
100 else
101 this->construct(std::move(rhs.mValue));
105 /* Define copy and move constructors and assignment operators, which may or may
106 * not be trivial.
108 template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
109 bool trivial_move = std::is_trivially_move_constructible<T>::value,
110 /* Trivial assignment is dependent on trivial construction+destruction. */
111 bool = trivial_copy && std::is_trivially_copy_assignable<T>::value
112 && std::is_trivially_destructible<T>::value,
113 bool = trivial_move && std::is_trivially_move_assignable<T>::value
114 && std::is_trivially_destructible<T>::value>
115 struct optional_storage;
117 /* Some versions of GCC have issues with 'this' in the following noexcept(...)
118 * statements, so this macro is a workaround.
120 #define _this std::declval<optional_storage*>()
122 /* Completely trivial. */
123 template<typename T>
124 struct optional_storage<T, true, true, true, true> : public optstore_helper<T> {
125 using optstore_helper<T>::optstore_helper;
126 constexpr optional_storage() noexcept = default;
127 constexpr optional_storage(const optional_storage&) = default;
128 constexpr optional_storage(optional_storage&&) = default;
129 constexpr optional_storage& operator=(const optional_storage&) = default;
130 constexpr optional_storage& operator=(optional_storage&&) = default;
133 /* Non-trivial move assignment. */
134 template<typename T>
135 struct optional_storage<T, true, true, true, false> : public optstore_helper<T> {
136 using optstore_helper<T>::optstore_helper;
137 constexpr optional_storage() noexcept = default;
138 constexpr optional_storage(const optional_storage&) = default;
139 constexpr optional_storage(optional_storage&&) = default;
140 constexpr optional_storage& operator=(const optional_storage&) = default;
141 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
142 { this->assign(std::move(rhs)); return *this; }
145 /* Non-trivial move construction. */
146 template<typename T>
147 struct optional_storage<T, true, false, true, false> : public optstore_helper<T> {
148 using optstore_helper<T>::optstore_helper;
149 constexpr optional_storage() noexcept = default;
150 constexpr optional_storage(const optional_storage&) = default;
151 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
152 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
153 constexpr optional_storage& operator=(const optional_storage&) = default;
154 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
155 { this->assign(std::move(rhs)); return *this; }
158 /* Non-trivial copy assignment. */
159 template<typename T>
160 struct optional_storage<T, true, true, false, true> : public optstore_helper<T> {
161 using optstore_helper<T>::optstore_helper;
162 constexpr optional_storage() noexcept = default;
163 constexpr optional_storage(const optional_storage&) = default;
164 constexpr optional_storage(optional_storage&&) = default;
165 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
166 { this->assign(rhs); return *this; }
167 constexpr optional_storage& operator=(optional_storage&&) = default;
170 /* Non-trivial copy construction. */
171 template<typename T>
172 struct optional_storage<T, false, true, false, true> : public optstore_helper<T> {
173 using optstore_helper<T>::optstore_helper;
174 constexpr optional_storage() noexcept = default;
175 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
176 { if(rhs.mHasValue) this->construct(rhs.mValue); }
177 constexpr optional_storage(optional_storage&&) = default;
178 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
179 { this->assign(rhs); return *this; }
180 constexpr optional_storage& operator=(optional_storage&&) = default;
183 /* Non-trivial assignment. */
184 template<typename T>
185 struct optional_storage<T, true, true, false, false> : public optstore_helper<T> {
186 using optstore_helper<T>::optstore_helper;
187 constexpr optional_storage() noexcept = default;
188 constexpr optional_storage(const optional_storage&) = default;
189 constexpr optional_storage(optional_storage&&) = default;
190 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
191 { this->assign(rhs); return *this; }
192 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
193 { this->assign(std::move(rhs)); return *this; }
196 /* Non-trivial assignment, non-trivial move construction. */
197 template<typename T>
198 struct optional_storage<T, true, false, false, false> : public optstore_helper<T> {
199 using optstore_helper<T>::optstore_helper;
200 constexpr optional_storage() noexcept = default;
201 constexpr optional_storage(const optional_storage&) = default;
202 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
203 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
204 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
205 { this->assign(rhs); return *this; }
206 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
207 { this->assign(std::move(rhs)); return *this; }
210 /* Non-trivial assignment, non-trivial copy construction. */
211 template<typename T>
212 struct optional_storage<T, false, true, false, false> : public optstore_helper<T> {
213 using optstore_helper<T>::optstore_helper;
214 constexpr optional_storage() noexcept = default;
215 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
216 { if(rhs.mHasValue) this->construct(rhs.mValue); }
217 constexpr optional_storage(optional_storage&&) = default;
218 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
219 { this->assign(rhs); return *this; }
220 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
221 { this->assign(std::move(rhs)); return *this; }
224 /* Completely non-trivial. */
225 template<typename T>
226 struct optional_storage<T, false, false, false, false> : public optstore_helper<T> {
227 using optstore_helper<T>::optstore_helper;
228 constexpr optional_storage() noexcept = default;
229 constexpr optional_storage(const optional_storage &rhs) NOEXCEPT_AS(_this->construct(rhs.mValue))
230 { if(rhs.mHasValue) this->construct(rhs.mValue); }
231 constexpr optional_storage(optional_storage&& rhs) NOEXCEPT_AS(_this->construct(std::move(rhs.mValue)))
232 { if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
233 constexpr optional_storage& operator=(const optional_storage &rhs) NOEXCEPT_AS(_this->assign(rhs))
234 { this->assign(rhs); return *this; }
235 constexpr optional_storage& operator=(optional_storage&& rhs) NOEXCEPT_AS(_this->assign(std::move(rhs)))
236 { this->assign(std::move(rhs)); return *this; }
239 #undef _this
241 } // namespace detail_
243 #define REQUIRES(...) std::enable_if_t<(__VA_ARGS__),bool> = true
245 template<typename T>
246 class optional {
247 using storage_t = detail_::optional_storage<T>;
249 storage_t mStore{};
251 public:
252 using value_type = T;
254 constexpr optional() = default;
255 constexpr optional(const optional&) = default;
256 constexpr optional(optional&&) = default;
257 constexpr optional(nullopt_t) noexcept { }
258 template<typename ...Args>
259 constexpr explicit optional(in_place_t, Args&& ...args)
260 NOEXCEPT_AS(storage_t{al::in_place, std::forward<Args>(args)...})
261 : mStore{al::in_place, std::forward<Args>(args)...}
263 template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
264 && !std::is_same<std::decay_t<U>, al::in_place_t>::value
265 && !std::is_same<std::decay_t<U>, optional<T>>::value
266 && std::is_convertible<U&&, T>::value)>
267 constexpr optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
268 : mStore{al::in_place, std::forward<U>(rhs)}
270 template<typename U, REQUIRES(std::is_constructible<T, U&&>::value
271 && !std::is_same<std::decay_t<U>, al::in_place_t>::value
272 && !std::is_same<std::decay_t<U>, optional<T>>::value
273 && !std::is_convertible<U&&, T>::value)>
274 constexpr explicit optional(U&& rhs) NOEXCEPT_AS(storage_t{al::in_place, std::forward<U>(rhs)})
275 : mStore{al::in_place, std::forward<U>(rhs)}
277 ~optional() = default;
279 constexpr optional& operator=(const optional&) = default;
280 constexpr optional& operator=(optional&&) = default;
281 constexpr optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
282 template<typename U=T>
283 constexpr std::enable_if_t<std::is_constructible<T, U>::value
284 && std::is_assignable<T&, U>::value
285 && !std::is_same<std::decay_t<U>, optional<T>>::value
286 && (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
287 optional&> operator=(U&& rhs)
289 if(mStore.mHasValue)
290 mStore.mValue = std::forward<U>(rhs);
291 else
292 mStore.construct(std::forward<U>(rhs));
293 return *this;
296 constexpr const T* operator->() const { return std::addressof(mStore.mValue); }
297 constexpr T* operator->() { return std::addressof(mStore.mValue); }
298 constexpr const T& operator*() const& { return mStore.mValue; }
299 constexpr T& operator*() & { return mStore.mValue; }
300 constexpr const T&& operator*() const&& { return std::move(mStore.mValue); }
301 constexpr T&& operator*() && { return std::move(mStore.mValue); }
303 constexpr explicit operator bool() const noexcept { return mStore.mHasValue; }
304 constexpr bool has_value() const noexcept { return mStore.mHasValue; }
306 constexpr T& value() & { return mStore.mValue; }
307 constexpr const T& value() const& { return mStore.mValue; }
308 constexpr T&& value() && { return std::move(mStore.mValue); }
309 constexpr const T&& value() const&& { return std::move(mStore.mValue); }
311 template<typename U>
312 constexpr T value_or(U&& defval) const&
313 { return bool(*this) ? **this : static_cast<T>(std::forward<U>(defval)); }
314 template<typename U>
315 constexpr T value_or(U&& defval) &&
316 { return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
318 template<typename ...Args>
319 constexpr T& emplace(Args&& ...args)
321 mStore.reset();
322 mStore.construct(std::forward<Args>(args)...);
323 return mStore.mValue;
325 template<typename U, typename ...Args>
326 constexpr std::enable_if_t<std::is_constructible<T, std::initializer_list<U>&, Args&&...>::value,
327 T&> emplace(std::initializer_list<U> il, Args&& ...args)
329 mStore.reset();
330 mStore.construct(il, std::forward<Args>(args)...);
331 return mStore.mValue;
334 constexpr void reset() noexcept { mStore.reset(); }
337 template<typename T>
338 constexpr optional<std::decay_t<T>> make_optional(T&& arg)
339 { return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
341 template<typename T, typename... Args>
342 constexpr optional<T> make_optional(Args&& ...args)
343 { return optional<T>{in_place, std::forward<Args>(args)...}; }
345 template<typename T, typename U, typename... Args>
346 constexpr optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
347 { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
349 #undef REQUIRES
350 #undef NOEXCEPT_AS
351 } // namespace al
353 #endif /* AL_OPTIONAL_H */