aboutsummaryrefslogtreecommitdiff
path: root/lib/util.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/util.h')
-rw-r--r--lib/util.h195
1 files changed, 133 insertions, 62 deletions
diff --git a/lib/util.h b/lib/util.h
index 13eec143..beb3c697 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -18,8 +18,9 @@
#pragma once
-#include <QtCore/QPointer>
-#if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
+#include <QtCore/QLatin1String>
+
+#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
#include <QtCore/QMetaEnum>
#include <QtCore/QDebug>
#endif
@@ -27,10 +28,12 @@
#include <functional>
#include <memory>
-#if __cplusplus >= 201703L
+#if __has_cpp_attribute(fallthrough)
#define FALLTHROUGH [[fallthrough]]
#elif __has_cpp_attribute(clang::fallthrough)
#define FALLTHROUGH [[clang::fallthrough]]
+#elif __has_cpp_attribute(gnu::fallthrough)
+#define FALLTHROUGH [[gnu::fallthrough]]
#else
#define FALLTHROUGH // -fallthrough
#endif
@@ -49,6 +52,14 @@ template <typename T>
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
+#endif
+
namespace QMatrixClient
{
// The below enables pretty-printing of enums in logs
@@ -86,74 +97,150 @@ namespace QMatrixClient
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(std::decay_t<T>()), _omitted(true) { }
- Omittable(const std::decay_t<T>& val) : _value(val) { }
- Omittable(std::decay_t<T>&& val) : _value(std::move(val)) { }
- Omittable<T>& operator=(const std::decay_t<T>& val)
+ 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=(std::decay_t<T>&& val)
+ 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 std::decay_t<T>& value() const { Q_ASSERT(!_omitted); return _value; }
- std::decay_t<T>& value() { Q_ASSERT(!_omitted); return _value; }
- std::decay_t<T>&& release() { _omitted = true; return std::move(_value); }
+ 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); }
- operator bool() const { return !omitted(); }
- const std::decay<T>* operator->() const { return &value(); }
- std::decay_t<T>* operator->() { return &value(); }
- const std::decay_t<T>& operator*() const { return value(); }
- std::decay_t<T>& operator*() { return 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;
};
+ namespace _impl {
+ template <typename AlwaysVoid, typename> struct fn_traits;
+ }
+
/** Determine traits of an arbitrary function/lambda/functor
- * This only works with arity of 1 (1-argument) for now but is extendable
- * to other cases. Also, doesn't work with generic lambdas and function
- * objects that have operator() overloaded
+ * 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 function_traits<decltype(&T::operator())>
- { }; // A generic function object that has (non-overloaded) operator()
+ struct function_traits : public _impl::fn_traits<void, T> {};
// Specialisation for a function
- template <typename ReturnT, typename ArgT>
- struct function_traits<ReturnT(ArgT)>
+ template <typename ReturnT, typename... ArgTs>
+ struct function_traits<ReturnT(ArgTs...)>
{
+ static constexpr auto is_callable = true;
using return_type = ReturnT;
- using arg_type = ArgT;
+ using arg_types = std::tuple<ArgTs...>;
+ using function_type = std::function<ReturnT(ArgTs...)>;
+ static constexpr auto arg_number = std::tuple_size<arg_types>::value;
};
- // Specialisation for a member function
- template <typename ReturnT, typename ClassT, typename ArgT>
- struct function_traits<ReturnT(ClassT::*)(ArgT)>
- : function_traits<ReturnT(ArgT)>
- { };
+ 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 const member function
- template <typename ReturnT, typename ClassT, typename ArgT>
- struct function_traits<ReturnT(ClassT::*)(ArgT) const>
- : function_traits<ReturnT(ArgT)>
- { };
+ // 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>
- using fn_arg_t = typename function_traits<FnT>::arg_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)
{
@@ -209,36 +296,20 @@ namespace QMatrixClient
return std::make_pair(last, sLast);
}
- /** A guard pointer that disconnects an interested object upon destruction
- * It's almost QPointer<> except that you have to initialise it with one
- * more additional parameter - a pointer to a QObject that will be
- * disconnected from signals of the underlying pointer upon the guard's
- * destruction.
+ /** Sanitize the text before showing in HTML
+ * This does toHtmlEscaped() and removes Unicode BiDi marks.
*/
- template <typename T>
- class ConnectionsGuard : public QPointer<T>
- {
- public:
- ConnectionsGuard(T* publisher, QObject* subscriber)
- : QPointer<T>(publisher), subscriber(subscriber)
- { }
- ~ConnectionsGuard()
- {
- if (*this)
- (*this)->disconnect(subscriber);
- }
- ConnectionsGuard(ConnectionsGuard&&) = default;
- ConnectionsGuard& operator=(ConnectionsGuard&&) = default;
- Q_DISABLE_COPY(ConnectionsGuard)
- using QPointer<T>::operator=;
+ QString sanitized(const QString& plainText);
- private:
- QObject* subscriber;
- };
-
- /** Pretty-prints plain text into HTML
+ /** Pretty-print plain text into HTML
* This includes HTML escaping of <,>,",& and URLs linkification.
*/
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);
} // namespace QMatrixClient