aboutsummaryrefslogtreecommitdiffstats
path: root/serd/detail/Optional.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'serd/detail/Optional.hpp')
-rw-r--r--serd/detail/Optional.hpp127
1 files changed, 127 insertions, 0 deletions
diff --git a/serd/detail/Optional.hpp b/serd/detail/Optional.hpp
new file mode 100644
index 00000000..24a5acce
--- /dev/null
+++ b/serd/detail/Optional.hpp
@@ -0,0 +1,127 @@
+/*
+ Copyright 2019-2020 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef SERD_DETAIL_OPTIONAL_HPP
+#define SERD_DETAIL_OPTIONAL_HPP
+
+#include <cassert>
+#include <cstddef>
+#include <utility>
+
+namespace serd {
+namespace detail {
+
+struct ConstructNullOptional
+{
+};
+
+/**
+ A simple optional wrapper around a wrapped type with a pointer-like API
+
+ This works like a typical optional type, but only works with Wrapper types,
+ and exploits the fact that these are interally just pointers to avoid adding
+ space overhead for an "is_set" flag, like a generic optional class would.
+
+ Types must explicitly opt-in to being optional by providing a constructor
+ that takes a single ContructNullOptional argument. This constructor should
+ only be used by the Optional implementation, which guarantees that such an
+ object will not be used except by calling its cobj() method.
+*/
+template <typename T>
+class Optional
+{
+public:
+ using CType = typename T::CType;
+
+ Optional() : _value(nullptr) {}
+
+ // NOLINTNEXTLINE(hicpp-explicit-conversions, modernize-pass-by-value)
+ Optional(const T& value) : _value(value) {}
+
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ Optional(T&& value) : _value(std::move(value)) {}
+
+ template <typename U,
+ typename = typename std::enable_if<
+ std::is_convertible<U, T>::value>::type>
+ // NOLINTNEXTLINE(hicpp-explicit-conversions)
+ Optional(U&& value) : _value(std::forward<U>(value))
+ {
+ }
+
+ void reset() { _value = T{nullptr}; }
+
+ const T& operator*() const
+ {
+ assert(_value.cobj());
+ return _value;
+ }
+
+ T& operator*()
+ {
+ assert(_value.cobj());
+ return _value;
+ }
+
+ const T* operator->() const
+ {
+ assert(_value.cobj());
+ return &_value;
+ }
+
+ T* operator->()
+ {
+ assert(_value.cobj());
+ return &_value;
+ }
+
+ bool operator==(const Optional& optional)
+ {
+ return (!*this && !optional) ||
+ (*this && optional && _value == optional._value);
+ }
+
+ bool operator!=(const Optional& optional) { return !operator==(optional); }
+
+ explicit operator bool() const { return _value.cobj(); }
+ bool operator!() const { return !_value.cobj(); }
+
+ inline CType* cobj() { return _value.cobj(); }
+ inline const CType* cobj() const { return _value.cobj(); }
+
+private:
+ T _value;
+};
+
+} // namespace detail
+
+template <class T>
+constexpr detail::Optional<T>
+make_optional(T&& value)
+{
+ return detail::Optional<T>{std::forward<T>(value)};
+}
+
+template <class T, class... Args>
+constexpr detail::Optional<T>
+make_optional(Args&&... args)
+{
+ return detail::Optional<T>{std::forward<Args>(args)...};
+}
+
+} // namespace serd
+
+#endif // SERD_DETAIL_OPTIONAL_HPP