aboutsummaryrefslogtreecommitdiffstats
path: root/bindings/cpp/include/serd/Optional.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'bindings/cpp/include/serd/Optional.hpp')
-rw-r--r--bindings/cpp/include/serd/Optional.hpp164
1 files changed, 164 insertions, 0 deletions
diff --git a/bindings/cpp/include/serd/Optional.hpp b/bindings/cpp/include/serd/Optional.hpp
new file mode 100644
index 00000000..ee6a7fc2
--- /dev/null
+++ b/bindings/cpp/include/serd/Optional.hpp
@@ -0,0 +1,164 @@
+/*
+ Copyright 2019-2021 David Robillard <d@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_OPTIONAL_HPP
+#define SERD_OPTIONAL_HPP
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+namespace serd {
+
+/**
+ @defgroup serdpp_optional Optional
+ @ingroup serdpp
+ @{
+*/
+
+struct ConstructNullOptional {};
+
+/// Special tag for constructing an unset Optional
+struct Nullopt {
+ enum class Construct { internal };
+
+ explicit constexpr Nullopt(Construct) {}
+};
+
+/**
+ 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<class T>
+class Optional
+{
+public:
+ /// The type of the underlying C object
+ using CType = typename T::CType;
+
+ /// Constructs an empty optional
+ Optional() = default;
+
+ /// Constructs an empty optional
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ Optional(Nullopt) {}
+
+ /// Constructs an optional that contains the given value
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ Optional(T value)
+ : _value{std::move(value)}
+ {}
+
+ /// Constructs an optional that contains a converted value
+ template<
+ typename U,
+ typename = typename std::enable_if<std::is_convertible<U, T>::value>::type>
+ // NOLINTNEXTLINE(google-explicit-constructor, hicpp-explicit-conversions)
+ Optional(U&& value)
+ : _value{std::forward<U>(value)}
+ {}
+
+ /// Destroys any contained value
+ void reset() { _value = T{nullptr}; }
+
+ /// Accesses the contained value
+ const T& operator*() const
+ {
+ assert(_value.cobj());
+ return _value;
+ }
+
+ /// Accesses the contained value
+ T& operator*()
+ {
+ assert(_value.cobj());
+ return _value;
+ }
+
+ /// Accesses the contained value
+ const T* operator->() const
+ {
+ assert(_value.cobj());
+ return &_value;
+ }
+
+ /// Accesses the contained value
+ T* operator->()
+ {
+ assert(_value.cobj());
+ return &_value;
+ }
+
+ /// Tests if optional objects are equal
+ bool operator==(const Optional& optional)
+ {
+ return (!*this && !optional) ||
+ (*this && optional && _value == optional._value);
+ }
+
+ /// Tests if optional objects are not equal
+ bool operator!=(const Optional& optional) { return !operator==(optional); }
+
+ /// Returns true if this optional contains a value
+ explicit operator bool() const { return _value.cobj(); }
+
+ /// Returns true if this optional does not contain a value
+ bool operator!() const { return !_value.cobj(); }
+
+ /// Return a pointer to the underlying C object, or null
+ CType* cobj() { return _value.cobj(); }
+
+ /// Return a pointer to the underlying C object, or null
+ const CType* cobj() const { return _value.cobj(); }
+
+private:
+ T _value{nullptr};
+};
+
+/// Creates an optional object from `value`
+template<class T>
+constexpr Optional<std::decay_t<T>>
+make_optional(T&& value)
+{
+ return Optional<T>{std::forward<T>(value)};
+}
+
+/// Creates an optional object with a value constructed in-place from `args`
+template<class T, class... Args>
+constexpr Optional<T>
+make_optional(Args&&... args)
+{
+ return Optional<T>{std::forward<Args>(args)...};
+}
+
+/// Constant that represents an empty optional
+static constexpr Nullopt nullopt{Nullopt::Construct::internal};
+
+/**
+ @}
+*/
+
+} // namespace serd
+
+#endif // SERD_OPTIONAL_HPP