diff options
author | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-01-23 17:11:22 +0100 |
---|---|---|
committer | Alexey Rusakov <Kitsune-Ral@users.sf.net> | 2022-01-23 17:12:08 +0100 |
commit | abbab8d8f8c566bc2c9cdf766c6fbb11d978ca47 (patch) | |
tree | 07623282baa91122528a9b553370f8bb6195bfa0 /lib/util.h | |
parent | 63c953800330017ebb2afbabf41e5c4932c4d640 (diff) | |
download | libquotient-abbab8d8f8c566bc2c9cdf766c6fbb11d978ca47.tar.gz libquotient-abbab8d8f8c566bc2c9cdf766c6fbb11d978ca47.zip |
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
Diffstat (limited to 'lib/util.h')
-rw-r--r-- | lib/util.h | 141 |
1 files changed, 0 insertions, 141 deletions
@@ -9,10 +9,8 @@ #include <QtCore/QLatin1String> #include <QtCore/QHashFunctions> -#include <functional> #include <memory> #include <unordered_map> -#include <optional> #ifndef Q_DISABLE_MOVE // Q_DISABLE_MOVE was introduced in Q_VERSION_CHECK(5,13,0) @@ -52,145 +50,6 @@ struct HashQ { template <typename KeyT, typename ValT> using UnorderedMap = std::unordered_map<KeyT, ValT, HashQ<KeyT>>; -namespace _impl { - template <typename TT> - constexpr auto IsOmittableValue = false; - template <typename TT> - constexpr auto IsOmittable = IsOmittableValue<std::decay_t<TT>>; -} - -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<S> 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<int> innermost; }; - * struct Outer { int anotherMember = 10; Omittable<Inner> inner; }; - * Omittable<Outer> 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 <typename T> -class Omittable : public std::optional<T> { -public: - using base_type = std::optional<T>; - using value_type = std::decay_t<T>; - - using std::optional<T>::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 <typename T1> - auto merge(const Omittable<T1>& other) - -> std::enable_if_t<std::is_convertible_v<T1, 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 <typename T> -Omittable(T&&) -> Omittable<T>; - -namespace _impl { - template <typename T> - constexpr auto IsOmittableValue<Omittable<T>> = true; -} - -template <typename T1, typename T2> -inline auto merge(Omittable<T1>& lhs, T2&& rhs) -{ - return lhs.merge(std::forward<T2>(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 <typename T1, typename T2> -inline auto merge(T1& lhs, const Omittable<T2>& rhs) - -> std::enable_if_t<!_impl::IsOmittable<T1> - && std::is_convertible_v<T2, T1>, 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)); |