Don't assume the total length of certain arrays
[openal-soft.git] / common / aloptional.h
blob269cba0e93e0a1f27edb045a5b4884bf81aa639a
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 #define REQUIRES(...) bool rt_=true, typename std::enable_if<rt_ && (__VA_ARGS__),bool>::type = true
14 struct nullopt_t { };
15 struct in_place_t { };
17 constexpr nullopt_t nullopt{};
18 constexpr in_place_t in_place{};
20 template<typename T>
21 class optional {
22 bool mHasValue{false};
23 union {
24 char mDummy[sizeof(T)]{};
25 T mValue;
28 template<typename... Args>
29 void DoConstruct(Args&& ...args)
31 ::new (std::addressof(mValue)) T{std::forward<Args>(args)...};
32 mHasValue = true;
35 public:
36 using value_type = T;
38 optional() noexcept = default;
39 optional(nullopt_t) noexcept { }
40 template<REQUIRES(std::is_copy_constructible<T>::value)>
41 optional(const optional &rhs) { if(rhs) DoConstruct(*rhs); }
42 template<REQUIRES(std::is_move_constructible<T>::value)>
43 optional(optional&& rhs) { if(rhs) DoConstruct(std::move(*rhs)); }
44 template<typename... Args, REQUIRES(std::is_constructible<T, Args...>::value)>
45 explicit optional(in_place_t, Args&& ...args) : mHasValue{true}
46 , mValue{std::forward<Args>(args)...}
47 { }
48 template<typename U, typename... Args, REQUIRES(std::is_constructible<T, std::initializer_list<U>&, Args...>::value)>
49 explicit optional(in_place_t, std::initializer_list<U> il, Args&& ...args)
50 : mHasValue{true}, mValue{il, std::forward<Args>(args)...}
51 { }
52 template<typename U=value_type, REQUIRES(std::is_constructible<T, U&&>::value &&
53 !std::is_same<typename std::decay<U>::type, in_place_t>::value &&
54 !std::is_same<typename std::decay<U>::type, optional<T>>::value &&
55 std::is_constructible<U&&, T>::value)>
56 constexpr explicit optional(U&& value) : mHasValue{true}, mValue{std::forward<U>(value)}
57 { }
58 template<typename U=value_type, REQUIRES(std::is_constructible<T, U&&>::value &&
59 !std::is_same<typename std::decay<U>::type, in_place_t>::value &&
60 !std::is_same<typename std::decay<U>::type, optional<T>>::value &&
61 !std::is_constructible<U&&, T>::value)>
62 constexpr optional(U&& value) : mHasValue{true}, mValue{std::forward<U>(value)}
63 { }
64 ~optional() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
66 optional& operator=(nullopt_t) noexcept { reset(); return *this; }
67 template<REQUIRES(std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value)>
68 optional& operator=(const optional &rhs)
70 if(!rhs)
71 reset();
72 else if(*this)
73 mValue = *rhs;
74 else
75 DoConstruct(*rhs);
76 return *this;
78 template<REQUIRES(std::is_move_constructible<T>::value && std::is_move_assignable<T>::value)>
79 optional& operator=(optional&& rhs)
81 if(!rhs)
82 reset();
83 else if(*this)
84 mValue = std::move(*rhs);
85 else
86 DoConstruct(std::move(*rhs));
87 return *this;
89 template<typename U=T, REQUIRES(std::is_constructible<T, U>::value &&
90 std::is_assignable<T&, U>::value &&
91 !std::is_same<typename std::decay<U>::type, optional<T>>::value &&
92 (!std::is_same<typename std::decay<U>::type, T>::value ||
93 !std::is_scalar<U>::value))>
94 optional& operator=(U&& rhs)
96 if(*this)
97 mValue = std::forward<U>(rhs);
98 else
99 DoConstruct(std::forward<U>(rhs));
100 return *this;
103 const T* operator->() const { return std::addressof(mValue); }
104 T* operator->() { return std::addressof(mValue); }
105 const T& operator*() const& { return mValue; }
106 T& operator*() & { return mValue; }
107 const T&& operator*() const&& { return std::move(mValue); }
108 T&& operator*() && { return std::move(mValue); }
110 operator bool() const noexcept { return mHasValue; }
111 bool has_value() const noexcept { return mHasValue; }
113 T& value() & { return mValue; }
114 const T& value() const& { return mValue; }
115 T&& value() && { return std::move(mValue); }
116 const T&& value() const&& { return std::move(mValue); }
118 template<typename U>
119 T value_or(U&& defval) const&
120 { return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
121 template<typename U>
122 T value_or(U&& defval) &&
123 { return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
125 void reset() noexcept
127 if(mHasValue)
128 al::destroy_at(std::addressof(mValue));
129 mHasValue = false;
133 template<typename T>
134 inline optional<typename std::decay<T>::type> make_optional(T&& arg)
135 { return optional<typename std::decay<T>::type>{in_place, std::forward<T>(arg)}; }
137 template<typename T, typename... Args>
138 inline optional<T> make_optional(Args&& ...args)
139 { return optional<T>{in_place, std::forward<Args>(args)...}; }
141 template<typename T, typename U, typename... Args>
142 inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
143 { return optional<T>{in_place, il, std::forward<Args>(args)...}; }
145 #undef REQUIRES
147 } // namespace al
149 #endif /* AL_SPAN_H */