diff options
Diffstat (limited to 'lib/util.h')
-rw-r--r-- | lib/util.h | 543 |
1 files changed, 285 insertions, 258 deletions
@@ -13,7 +13,7 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #pragma once @@ -21,312 +21,339 @@ #include <QtCore/QLatin1String> #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) -#include <QtCore/QMetaEnum> -#include <QtCore/QDebug> +# include <QtCore/QDebug> +# include <QtCore/QMetaEnum> #endif #include <functional> #include <memory> #if __has_cpp_attribute(fallthrough) -#define FALLTHROUGH [[fallthrough]] +# define FALLTHROUGH [[fallthrough]] #elif __has_cpp_attribute(clang::fallthrough) -#define FALLTHROUGH [[clang::fallthrough]] +# define FALLTHROUGH [[clang::fallthrough]] #elif __has_cpp_attribute(gnu::fallthrough) -#define FALLTHROUGH [[gnu::fallthrough]] +# define FALLTHROUGH [[gnu::fallthrough]] #else -#define FALLTHROUGH // -fallthrough +# define FALLTHROUGH // -fallthrough #endif // Along the lines of Q_DISABLE_COPY -#define DISABLE_MOVE(_ClassName) \ +#define DISABLE_MOVE(_ClassName) \ _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; #if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) // Copy-pasted from Qt 5.10 template <typename T> -Q_DECL_CONSTEXPR typename std::add_const<T>::type &qAsConst(T &t) Q_DECL_NOTHROW { return t; } +Q_DECL_CONSTEXPR typename std::add_const<T>::type& qAsConst(T& t) Q_DECL_NOTHROW +{ + return t; +} // prevent rvalue arguments: template <typename T> -static void qAsConst(const T &&) Q_DECL_EQ_DELETE; +static void qAsConst(const T&&) Q_DECL_EQ_DELETE; #endif // MSVC 2015 and older GCC's don't handle initialisation from initializer lists // right in the absense of a constructor; MSVC 2015, notably, fails with // "error C2440: 'return': cannot convert from 'initializer list' to '<type>'" -#if (defined(_MSC_VER) && _MSC_VER < 1910) || \ - (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) -# define BROKEN_INITIALIZER_LISTS +#if (defined(_MSC_VER) && _MSC_VER < 1910) \ + || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) +# define BROKEN_INITIALIZER_LISTS #endif namespace QMatrixClient { - // The below enables pretty-printing of enums in logs +// The below enables pretty-printing of enums in logs #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)) -#define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) +# define REGISTER_ENUM(EnumName) Q_ENUM(EnumName) #else - // Thanks to Olivier for spelling it and for making Q_ENUM to replace it: - // https://woboq.com/blog/q_enum.html -#define REGISTER_ENUM(EnumName) \ - Q_ENUMS(EnumName) \ - friend QDebug operator<<(QDebug dbg, EnumName val) \ - { \ - static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ - return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey(int(val)); \ - } +// Thanks to Olivier for spelling it and for making Q_ENUM to replace it: +// https://woboq.com/blog/q_enum.html +# define REGISTER_ENUM(EnumName) \ + Q_ENUMS(EnumName) \ + friend QDebug operator<<(QDebug dbg, EnumName val) \ + { \ + static int enumIdx = staticMetaObject.indexOfEnumerator(#EnumName); \ + return dbg << Event::staticMetaObject.enumerator(enumIdx).valueToKey( \ + int(val)); \ + } #endif - /** static_cast<> for unique_ptr's */ - template <typename T1, typename PtrT2> - inline auto unique_ptr_cast(PtrT2&& p) +/** static_cast<> for unique_ptr's */ +template <typename T1, typename PtrT2> +inline auto unique_ptr_cast(PtrT2&& p) +{ + return std::unique_ptr<T1>(static_cast<T1*>(p.release())); +} + +struct NoneTag +{}; +constexpr NoneTag none {}; + +/** A crude substitute for `optional` while we're not C++17 + * + * Only works with default-constructible types. + */ +template <typename T> +class Omittable +{ + static_assert(!std::is_reference<T>::value, + "You cannot make an Omittable<> with a reference type"); + +public: + using value_type = std::decay_t<T>; + + explicit Omittable() + : Omittable(none) + {} + Omittable(NoneTag) + : _value(value_type()) + , _omitted(true) + {} + Omittable(const value_type& val) + : _value(val) + {} + Omittable(value_type&& val) + : _value(std::move(val)) + {} + Omittable<T>& operator=(const value_type& val) { - return std::unique_ptr<T1>(static_cast<T1*>(p.release())); + _value = val; + _omitted = false; + return *this; + } + Omittable<T>& operator=(value_type&& val) + { + // For some reason GCC complains about -Wmaybe-uninitialized + // in the context of using Omittable<bool> with converters.h; + // though the logic looks very much benign (GCC bug???) + _value = std::move(val); + _omitted = false; + return *this; } - struct NoneTag {}; - constexpr NoneTag none {}; - - /** A crude substitute for `optional` while we're not C++17 - * - * Only works with default-constructible types. - */ - template <typename T> - class Omittable + bool operator==(const value_type& rhs) const { - static_assert(!std::is_reference<T>::value, - "You cannot make an Omittable<> with a reference type"); - public: - using value_type = std::decay_t<T>; - - explicit Omittable() : Omittable(none) { } - Omittable(NoneTag) : _value(value_type()), _omitted(true) { } - Omittable(const value_type& val) : _value(val) { } - Omittable(value_type&& val) : _value(std::move(val)) { } - Omittable<T>& operator=(const value_type& val) - { - _value = val; - _omitted = false; - return *this; - } - Omittable<T>& operator=(value_type&& val) - { - // For some reason GCC complains about -Wmaybe-uninitialized - // in the context of using Omittable<bool> with converters.h; - // though the logic looks very much benign (GCC bug???) - _value = std::move(val); - _omitted = false; - return *this; - } - - bool operator==(const value_type& rhs) const - { - return !omitted() && value() == rhs; - } - friend bool operator==(const value_type& lhs, - const Omittable<value_type>& rhs) - { - return rhs == lhs; - } - bool operator!=(const value_type& rhs) const - { - return !operator==(rhs); - } - friend bool operator!=(const value_type& lhs, - const Omittable<value_type>& rhs) - { - return !(rhs == lhs); - } - - bool omitted() const { return _omitted; } - const value_type& value() const - { - Q_ASSERT(!_omitted); - return _value; - } - value_type& editValue() - { - _omitted = false; - return _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<T1, T>::value, bool> - { - if (other.omitted() || - (!_omitted && _value == other.value())) - return false; - _omitted = false; - _value = other.value(); - return true; - } - value_type&& release() { _omitted = true; return std::move(_value); } - - const value_type* operator->() const & { return &value(); } - value_type* operator->() & { return &editValue(); } - const value_type& operator*() const & { return value(); } - value_type& operator*() & { return editValue(); } - - private: - T _value; - bool _omitted = false; - }; + return !omitted() && value() == rhs; + } + friend bool operator==(const value_type& lhs, + const Omittable<value_type>& rhs) + { + return rhs == lhs; + } + bool operator!=(const value_type& rhs) const { return !operator==(rhs); } + friend bool operator!=(const value_type& lhs, + const Omittable<value_type>& rhs) + { + return !(rhs == lhs); + } - namespace _impl { - template <typename AlwaysVoid, typename> struct fn_traits; + bool omitted() const { return _omitted; } + const value_type& value() const + { + Q_ASSERT(!_omitted); + return _value; + } + value_type& editValue() + { + _omitted = false; + return _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<T1, T>::value, bool> + { + if (other.omitted() || (!_omitted && _value == other.value())) + return false; + _omitted = false; + _value = other.value(); + return true; + } + value_type&& release() + { + _omitted = true; + return std::move(_value); } - /** Determine traits of an arbitrary function/lambda/functor - * Doesn't work with generic lambdas and function objects that have - * operator() overloaded. - * \sa https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 - */ - template <typename T> - struct function_traits : public _impl::fn_traits<void, T> {}; + const value_type* operator->() const& { return &value(); } + value_type* operator->() & { return &editValue(); } + const value_type& operator*() const& { return value(); } + value_type& operator*() & { return editValue(); } - // Specialisation for a function - template <typename ReturnT, typename... ArgTs> - struct function_traits<ReturnT(ArgTs...)> - { - static constexpr auto is_callable = true; - using return_type = ReturnT; - using arg_types = std::tuple<ArgTs...>; - using function_type = std::function<ReturnT(ArgTs...)>; - static constexpr auto arg_number = std::tuple_size<arg_types>::value; - }; +private: + T _value; + bool _omitted = false; +}; + +namespace _impl +{ + template <typename AlwaysVoid, typename> + struct fn_traits; +} + +/** Determine traits of an arbitrary function/lambda/functor + * Doesn't work with generic lambdas and function objects that have + * operator() overloaded. + * \sa + * https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765 + */ +template <typename T> +struct function_traits : public _impl::fn_traits<void, T> +{}; - namespace _impl { - template <typename AlwaysVoid, typename T> - struct fn_traits - { - static constexpr auto is_callable = false; - }; - - template <typename T> - struct fn_traits<decltype(void(&T::operator())), T> - : public fn_traits<void, decltype(&T::operator())> - { }; // A generic function object that has (non-overloaded) operator() - - // Specialisation for a member function - template <typename ReturnT, typename ClassT, typename... ArgTs> - struct fn_traits<void, ReturnT(ClassT::*)(ArgTs...)> - : function_traits<ReturnT(ArgTs...)> - { }; - - // Specialisation for a const member function - template <typename ReturnT, typename ClassT, typename... ArgTs> - struct fn_traits<void, ReturnT(ClassT::*)(ArgTs...) const> - : function_traits<ReturnT(ArgTs...)> - { }; - } // namespace _impl - - template <typename FnT> - using fn_return_t = typename function_traits<FnT>::return_type; - - template <typename FnT, int ArgN = 0> - using fn_arg_t = - std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>; - - template <typename R, typename FnT> - constexpr bool returns() +// Specialisation for a function +template <typename ReturnT, typename... ArgTs> +struct function_traits<ReturnT(ArgTs...)> +{ + static constexpr auto is_callable = true; + using return_type = ReturnT; + using arg_types = std::tuple<ArgTs...>; + using function_type = std::function<ReturnT(ArgTs...)>; + static constexpr auto arg_number = std::tuple_size<arg_types>::value; +}; + +namespace _impl +{ + template <typename AlwaysVoid, typename T> + struct fn_traits { - return std::is_same<fn_return_t<FnT>, R>::value; - } + static constexpr auto is_callable = false; + }; - // Poor-man's is_invokable template <typename T> - constexpr auto is_callable_v = function_traits<T>::is_callable; + struct fn_traits<decltype(void(&T::operator())), T> + : public fn_traits<void, decltype(&T::operator())> + {}; // A generic function object that has (non-overloaded) operator() + + // Specialisation for a member function + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_traits<void, ReturnT (ClassT::*)(ArgTs...)> + : function_traits<ReturnT(ArgTs...)> + {}; + + // Specialisation for a const member function + template <typename ReturnT, typename ClassT, typename... ArgTs> + struct fn_traits<void, ReturnT (ClassT::*)(ArgTs...) const> + : function_traits<ReturnT(ArgTs...)> + {}; +} // namespace _impl + +template <typename FnT> +using fn_return_t = typename function_traits<FnT>::return_type; + +template <typename FnT, int ArgN = 0> +using fn_arg_t = + std::tuple_element_t<ArgN, typename function_traits<FnT>::arg_types>; + +template <typename R, typename FnT> +constexpr bool returns() +{ + return std::is_same<fn_return_t<FnT>, R>::value; +} + +// Poor-man's is_invokable +template <typename T> +constexpr auto is_callable_v = function_traits<T>::is_callable; + +inline auto operator"" _ls(const char* s, std::size_t size) +{ + return QLatin1String(s, int(size)); +} - inline auto operator"" _ls(const char* s, std::size_t size) +/** An abstraction over a pair of iterators + * This is a very basic range type over a container with iterators that + * are at least ForwardIterators. Inspired by Ranges TS. + */ +template <typename ArrayT> +class Range +{ + // Looking forward for Ranges TS to produce something (in C++23?..) + using iterator = typename ArrayT::iterator; + using const_iterator = typename ArrayT::const_iterator; + using size_type = typename ArrayT::size_type; + +public: + Range(ArrayT& arr) + : from(std::begin(arr)) + , to(std::end(arr)) + {} + Range(iterator from, iterator to) + : from(from) + , to(to) + {} + + size_type size() const { - return QLatin1String(s, int(size)); + Q_ASSERT(std::distance(from, to) >= 0); + return size_type(std::distance(from, to)); } + bool empty() const { return from == to; } + const_iterator begin() const { return from; } + const_iterator end() const { return to; } + iterator begin() { return from; } + iterator end() { return to; } + +private: + iterator from; + iterator to; +}; + +/** A replica of std::find_first_of that returns a pair of iterators + * + * Convenient for cases when you need to know which particular "first of" + * [sFirst, sLast) has been found in [first, last). + */ +template <typename InputIt, typename ForwardIt, typename Pred> +inline std::pair<InputIt, ForwardIt> findFirstOf(InputIt first, InputIt last, + ForwardIt sFirst, + ForwardIt sLast, Pred pred) +{ + for (; first != last; ++first) + for (auto it = sFirst; it != sLast; ++it) + if (pred(*first, *it)) + return std::make_pair(first, it); - /** An abstraction over a pair of iterators - * This is a very basic range type over a container with iterators that - * are at least ForwardIterators. Inspired by Ranges TS. - */ - template <typename ArrayT> - class Range - { - // Looking forward for Ranges TS to produce something (in C++23?..) - using iterator = typename ArrayT::iterator; - using const_iterator = typename ArrayT::const_iterator; - using size_type = typename ArrayT::size_type; - public: - Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) { } - Range(iterator from, iterator to) : from(from), to(to) { } - - size_type size() const - { - Q_ASSERT(std::distance(from, to) >= 0); - return size_type(std::distance(from, to)); - } - bool empty() const { return from == to; } - const_iterator begin() const { return from; } - const_iterator end() const { return to; } - iterator begin() { return from; } - iterator end() { return to; } - - private: - iterator from; - iterator to; - }; + return std::make_pair(last, sLast); +} - /** A replica of std::find_first_of that returns a pair of iterators - * - * Convenient for cases when you need to know which particular "first of" - * [sFirst, sLast) has been found in [first, last). - */ - template<typename InputIt, typename ForwardIt, typename Pred> - inline std::pair<InputIt, ForwardIt> findFirstOf( - InputIt first, InputIt last, ForwardIt sFirst, ForwardIt sLast, - Pred pred) - { - for (; first != last; ++first) - for (auto it = sFirst; it != sLast; ++it) - if (pred(*first, *it)) - return std::make_pair(first, it); +/** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */ +void linkifyUrls(QString& htmlEscapedText); - return std::make_pair(last, sLast); - } +/** Sanitize the text before showing in HTML + * + * This does toHtmlEscaped() and removes Unicode BiDi marks. + */ +QString sanitized(const QString& plainText); + +/** Pretty-print plain text into HTML + * + * This includes HTML escaping of <,>,",& and calling linkifyUrls() + */ +QString prettyPrint(const QString& plainText); + +/** Return a path to cache directory after making sure that it exists + * + * The returned path has a trailing slash, clients don't need to append it. + * \param dir path to cache directory relative to the standard cache path + */ +QString cacheLocation(const QString& dirName); + +/** Hue color component of based of the hash of the string. + * + * The implementation is based on XEP-0392: + * https://xmpp.org/extensions/xep-0392.html + * Naming and range are the same as QColor's hueF method: + * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision + */ +qreal stringToHueF(const QString& string); - /** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */ - void linkifyUrls(QString& htmlEscapedText); - - /** Sanitize the text before showing in HTML - * - * This does toHtmlEscaped() and removes Unicode BiDi marks. - */ - QString sanitized(const QString& plainText); - - /** Pretty-print plain text into HTML - * - * This includes HTML escaping of <,>,",& and calling linkifyUrls() - */ - QString prettyPrint(const QString& plainText); - - /** Return a path to cache directory after making sure that it exists - * - * The returned path has a trailing slash, clients don't need to append it. - * \param dir path to cache directory relative to the standard cache path - */ - QString cacheLocation(const QString& dirName); - - /** Hue color component of based of the hash of the string. - * - * The implementation is based on XEP-0392: - * https://xmpp.org/extensions/xep-0392.html - * Naming and range are the same as QColor's hueF method: - * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision - */ - qreal stringToHueF(const QString& string); - - /** Extract the serverpart from MXID */ - QString serverPart(const QString& mxId); -} // namespace QMatrixClient +/** Extract the serverpart from MXID */ +QString serverPart(const QString& mxId); +} // namespace QMatrixClient |