From abbab8d8f8c566bc2c9cdf766c6fbb11d978ca47 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 23 Jan 2022 17:11:22 +0100 Subject: Omittable: split out from util.h and refresh Improvements: - Quotient::lift() - a way to invoke a function on an optional (including Omittable) or a pointer if it's 'truthy'. Doesn't need enhanced function_traits<>, only the standard library; works on any number of arguments that can be dereferenced and casted to bool. - then() - the version of lift() as a member function. - edit() was renamed to ensure() (edit() might become a read-write counterpart of then() at some point). It's not really used across libQuotient codebase (or elsewhere) but is staying there just in case. It can also accept an initializer, removing the requirement of default-constructibility. - Quotient::merge() is simplified, with one universal implementation covering both Omittable/optional and plain values. - All that now lives in its dedicated pair of files, further decluttering util.h --- lib/util.h | 141 ------------------------------------------------------------- 1 file changed, 141 deletions(-) (limited to 'lib/util.h') diff --git a/lib/util.h b/lib/util.h index 3505b62f..753eb1ea 100644 --- a/lib/util.h +++ b/lib/util.h @@ -9,10 +9,8 @@ #include #include -#include #include #include -#include #ifndef Q_DISABLE_MOVE // Q_DISABLE_MOVE was introduced in Q_VERSION_CHECK(5,13,0) @@ -52,145 +50,6 @@ struct HashQ { template using UnorderedMap = std::unordered_map>; -namespace _impl { - template - constexpr auto IsOmittableValue = false; - template - constexpr auto IsOmittable = IsOmittableValue>; -} - -constexpr auto none = std::nullopt; - -/** `std::optional` with tweaks - * - * The tweaks are: - * - streamlined assignment (operator=)/emplace()ment of values that can be - * used to implicitly construct the underlying type, including - * direct-list-initialisation, e.g.: - * \code - * struct S { int a; char b; } - * Omittable o; - * o = { 1, 'a' }; // std::optional would require o = S { 1, 'a' } - * \endcode - * - entirely deleted value(). The technical reason is that Xcode 10 doesn't - * have it; but besides that, value_or() or (after explicit checking) - * `operator*()`/`operator->()` are better alternatives within Quotient - * that doesn't practice throwing exceptions (as doesn't most of Qt). - * - disabled non-const lvalue operator*() and operator->(), as it's too easy - * to inadvertently cause a value change through them. - * - edit() to provide a safe and explicit lvalue accessor instead of those - * above. Requires the underlying type to be default-constructible. - * Allows chained initialisation of nested Omittables: - * \code - * struct Inner { int member = 10; Omittable innermost; }; - * struct Outer { int anotherMember = 10; Omittable inner; }; - * Omittable o; // = { 10, std::nullopt }; - * o.edit().inner.edit().innermost.emplace(42); - * \endcode - * - merge() - a soft version of operator= that only overwrites its first - * operand with the second one if the second one is not empty. - */ -template -class Omittable : public std::optional { -public: - using base_type = std::optional; - using value_type = std::decay_t; - - using std::optional::optional; - - // Overload emplace() and operator=() to allow passing braced-init-lists - // (the standard emplace() does direct-initialisation but - // not direct-list-initialisation). - using base_type::operator=; - Omittable& operator=(const value_type& v) - { - base_type::operator=(v); - return *this; - } - Omittable& operator=(value_type&& v) - { - base_type::operator=(v); - return *this; - } - using base_type::emplace; - T& emplace(const T& val) { return base_type::emplace(val); } - T& emplace(T&& val) { return base_type::emplace(std::move(val)); } - - // use value_or() or check (with operator! or has_value) before accessing - // with operator-> or operator* - // The technical reason is that Xcode 10 has incomplete std::optional - // that has no value(); but using value() may also mean that you rely - // on the optional throwing an exception (which is not assumed practice - // throughout Quotient) or that you spend unnecessary CPU cycles on - // an extraneous has_value() check. - value_type& value() = delete; - const value_type& value() const = delete; - value_type& edit() - { - return this->has_value() ? base_type::operator*() : this->emplace(); - } - - [[deprecated("Use '!o' or '!o.has_value()' instead of 'o.omitted()'")]] - bool omitted() const - { - return !this->has_value(); - } - - //! Merge the value from another Omittable - //! \return true if \p other is not omitted and the value of - //! the current Omittable was different (or omitted), - //! in other words, if the current Omittable has changed; - //! false otherwise - template - auto merge(const Omittable& other) - -> std::enable_if_t, bool> - { - if (!other || (this->has_value() && **this == *other)) - return false; - emplace(*other); - return true; - } - - // Hide non-const lvalue operator-> and operator* as these are - // a bit too surprising: value() & doesn't lazy-create an object; - // and it's too easy to inadvertently change the underlying value. - - const value_type* operator->() const& { return base_type::operator->(); } - value_type* operator->() && { return base_type::operator->(); } - const value_type& operator*() const& { return base_type::operator*(); } - value_type& operator*() && { return base_type::operator*(); } -}; -template -Omittable(T&&) -> Omittable; - -namespace _impl { - template - constexpr auto IsOmittableValue> = true; -} - -template -inline auto merge(Omittable& lhs, T2&& rhs) -{ - return lhs.merge(std::forward(rhs)); -} - -//! \brief Merge the value from an Omittable -//! This is an adaptation of Omittable::merge() to the case when the value -//! on the left hand side is not an Omittable. -//! \return true if \p rhs is not omitted and the \p lhs value was different, -//! in other words, if \p lhs has changed; -//! false otherwise -template -inline auto merge(T1& lhs, const Omittable& rhs) - -> std::enable_if_t - && std::is_convertible_v, bool> -{ - if (!rhs || lhs == *rhs) - return false; - lhs = *rhs; - return true; -} - constexpr auto operator"" _ls(const char* s, std::size_t size) { return QLatin1String(s, int(size)); -- cgit v1.2.3