libQuotient
A Qt library for building matrix clients
Loading...
Searching...
No Matches
util.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2016 Kitsune Ral <kitsune-ral@users.sf.net>
2// SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru>
3// SPDX-License-Identifier: LGPL-2.1-or-later
4
5#pragma once
6
8
9#include <QtCore/QDebug>
10#include <QtCore/QElapsedTimer>
11#include <QtCore/QFuture>
12#include <QtCore/QHashFunctions>
13#include <QtCore/QLatin1String>
14#include <QtCore/QScopedPointer>
15#include <QtCore/QUrl>
16
17#include <memory>
18#include <optional>
19#include <source_location>
20#include <unordered_map>
21
22#define DECL_DEPRECATED_ENUMERATOR(Deprecated, Recommended)
23 Deprecated Q_DECL_ENUMERATOR_DEPRECATED_X("Use " #Recommended) = Recommended
24
25/// \brief Copy an object with slicing
26///
27/// Unintended slicing is bad, which is why there's a C++ Core Guideline that
28/// basically says "don't slice, or if you do, make it explicit". Sonar and
29/// clang-tidy have warnings matching this guideline; unfortunately, those
30/// warnings trigger even when you have a dedicated method (as the guideline
31/// recommends) that makes a slicing copy.
32///
33/// This macro is meant for cases when slicing is intended: the static cast
34/// silences the static analysis warning, and the macro appearance itself makes
35/// it very clear that slicing is wanted here. It is made as a macro
36/// (not as a function template) to support the case of private inheritance
37/// in which a function template would not be able to cast to the private base
38/// (see Uri::toUrl() for an example of just that situation).
39#define SLICE(Object, ToType) ToType{static_cast<const ToType&>(Object)}
40
41namespace Quotient {
42
43namespace _impl {
44 template <typename S>
45 constexpr inline auto toUtf8(S&& s)
46 {
47 if constexpr (std::convertible_to<S, std::string_view>)
48 return std::string_view(std::forward<S>(s));
49 else if constexpr (std::convertible_to<S, QByteArray>)
50 return QByteArray(std::forward<S>(s));
51 else //if constexpr (std::convertible_to<S, QString>)
52 return QString(std::forward<S>(s)).toUtf8();
53 }
54}
55
56//! \brief q(Utf8)Printable that can handle more than just QStrings
57//!
58//! This macro accepts all kinds of string-like input, from const char* all the way to raw
59//! QStringBuilder constructs. It returns a `const char*` pointer to a UTF-8 string; if the original
60//! input was QChar/u16-based, it creates a temporary buffer to store the UTF-8 representation that
61//! is destroyed once the statement containing QUO_CSTR() is done (therefore, ALWAYS copy the result
62//! based on QUO_CSTR() contents if you need to store it).
63#define QUO_CSTR(StringConvertible_) std::data(::Quotient::_impl::toUtf8(StringConvertible_))
64
65inline bool alarmX(bool alarmCondition, const auto& msg,
66 [[maybe_unused]] std::source_location loc = std::source_location::current())
67{
68 if (alarmCondition) [[unlikely]] {
69 qt_assert_x(loc.function_name(), QUO_CSTR(msg), loc.file_name(), loc.line());
70 qCritical() << msg;
71 }
72 return alarmCondition;
73}
74
75//! \brief A negative assertion facility that can be put in an if statement
76//!
77//! Unlike most other assertion functions or macros, this doesn't collapse to no-op in Release
78//! builds; rather, it sends a critical level message to the log and returns true so that you could
79//! immediately return or do some other damage control for Release builds. Also unlike most other
80//! assertion functions, \p AlarmCondition is the condition for failure, not for health; in other
81//! words, \p Message is sent to logs (and, in Debug configuration, the assertion fails)
82//! if \p AlarmCondition holds, not the other way around.
83//!
84//! This macro is a trivial wrapper around alarmX(), provided for API uniformity with QUO_ALARM()
85#define QUO_ALARM_X(...) ::Quotient::alarmX(__VA_ARGS__)
86
87#define QUO_ALARM(...) ::Quotient::alarmX((__VA_ARGS__) ? true : false, "Alarm: " #__VA_ARGS__)
88
89//! Evaluate the boolean expression and, in Debug mode, assert it to be true
90#define QUO_CHECK(...)
91 !::Quotient::alarmX(!(__VA_ARGS__) ? true : false, "Failing expression: " #__VA_ARGS__)
92
93//! A substitute for QtFuture::makeReadyVoidFuture() for compatibility with Qt pre-6.6
95{
96#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
97 return QtFuture::makeReadyVoidFuture();
98#else
99 return QtFuture::makeReadyFuture<void>();
100#endif
101}
102
103//! A substitute for QtFuture::makeReadyValueFuture() for compatibility with Qt pre-6.6
104template <typename T>
105inline auto makeReadyValueFuture(T&& value)
106{
107#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
108 return QtFuture::makeReadyValueFuture(std::forward<T>(value));
109#else
110 return QtFuture::makeReadyFuture(std::forward<T>(value));
111#endif
112}
113
114#if Quotient_VERSION_MAJOR == 0 && Quotient_VERSION_MINOR < 10
115/// This is only to make UnorderedMap alias work until we get rid of it
116template <typename T>
117struct HashQ {
118 size_t operator()(const T& s) const Q_DECL_NOEXCEPT
119 {
120 return qHash(s, uint(QHashSeed::globalSeed()));
121 }
122};
123/// A wrapper around std::unordered_map compatible with types that have qHash
124template <typename KeyT, typename ValT>
126 [[deprecated("Use std::unordered_map directly")]] = std::unordered_map<KeyT, ValT, HashQ<KeyT>>;
127
128inline namespace Literals { using namespace Qt::Literals; }
129
130#if Quotient_VERSION_MAJOR == 0 && Quotient_VERSION_MINOR > 9
131[[deprecated("Use operators from Qt::Literals (aka Quotient::Literals) instead")]]
132#endif
133constexpr auto operator""_ls(const char* s, std::size_t size)
134{
135 return operator""_L1(s, size);
136}
137
138template <typename ArrayT>
139class [[deprecated("Use std::ranges::subrange instead")]] Range {
140 using iterator = typename ArrayT::iterator;
141 using const_iterator = typename ArrayT::const_iterator;
142 using size_type = typename ArrayT::size_type;
143
144public:
145 constexpr Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {}
146 constexpr Range(iterator from, iterator to) : from(from), to(to) {}
147
148 constexpr size_type size() const
149 {
150 Q_ASSERT(std::distance(from, to) >= 0);
151 return size_type(std::distance(from, to));
152 }
153 constexpr bool empty() const { return from == to; }
154 constexpr const_iterator begin() const { return from; }
155 constexpr const_iterator end() const { return to; }
156 constexpr iterator begin() { return from; }
157 constexpr iterator end() { return to; }
158
159private:
160 iterator from;
161 iterator to;
162};
163
164namespace _impl {
165 template <typename T>
166 concept Holds_NonConst_LValue_Ref =
167 std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference<T>>;
168}
169
170//! \brief An adaptor for Qt (hash-)maps to make them iterable in STL style
171//!
172//! QMap/QHash container iterators returned by begin() and end() dereference
173//! to values, unlike STL where similar iterators dereference to key-value
174//! pairs. It is a problem in range-for if you want to also access map keys.
175//! This adaptor allows to use range-for syntax with access to both keys and
176//! values in QMap/QHash containers. Just use
177//! `for (auto&& [key, value] : asKeyValueRange(myMap)` instead of
178//! `for (auto&& value : myMap)`.
179//! \note When an rvalue is passed as the constructor argument, asKeyValueRange
180//! shallow-copies the map object to ensure its lifetime is maintained
181//! throughout the loop; with lvalues, no copying occurs, assuming that
182//! the map object outlives the range-for loop
183template <typename T>
184class [[deprecated(
185 "Use the member function with the same name of the respective container")]] asKeyValueRange {
186public:
187 explicit asKeyValueRange(T data)
188 : m_data { data }
189 {}
190
192 {
193 return m_data.keyValueBegin();
194 }
196 {
197 return m_data.keyValueEnd();
198 }
199 auto begin() const { return m_data.keyValueBegin(); }
200 auto end() const { return m_data.keyValueEnd(); }
201
202private:
203 T m_data;
204};
205
206// GCC complains about a deprecation even on deduction hints, so here's to suppress noise
208template <typename U>
210
211template <typename U>
213)
214#endif
215
216/** A replica of std::find_first_of that returns a pair of iterators
217 *
218 * Convenient for cases when you need to know which particular "first of"
219 * [sFirst, sLast) has been found in [first, last).
220 */
221template <typename InputIt, typename ForwardIt, typename Pred>
225{
226 for (; first != last; ++first)
227 for (auto it = sFirst; it != sLast; ++it)
228 if (pred(*first, *it))
229 return { first, it };
230
231 return { last, sLast };
232}
233
234//! \brief Common custom deleter for std::unique_ptr and QScopedPointer
235//!
236//! Since Qt 6, this is merely an alias for QScopedPointerDeleteLater (which is suitable
237//! for std::unique_ptr too).
239
240template <std::derived_from<QObject> T>
242
243//! \brief An owning implementation pointer
244//!
245//! This is basically std::unique_ptr<> to hold your pimpl's but without having
246//! to define default constructors/operator=() out of line.
247//! Thanks to https://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html
248//! for inspiration
249template <typename ImplType, typename TypeToDelete = ImplType>
250using ImplPtr = std::unique_ptr<ImplType, void (*)(TypeToDelete*)>;
251
252// Why this works (see also the link above): because this defers the moment
253// of requiring sizeof of ImplType to the place where makeImpl is invoked
254// (which is located, necessarily, in the .cpp file after ImplType definition).
255// The stock unique_ptr deleter (std::default_delete) normally needs sizeof
256// at the same spot - as long as you defer definition of the owning type
257// constructors and operator='s to the .cpp file as well. Which means you
258// have to explicitly declare and define them (even if with = default),
259// formally breaking the rule of zero; informally, just adding boilerplate code.
260// The custom deleter itself is instantiated at makeImpl invocation - there's
261// no way earlier to even know how ImplType will be deleted and whether that
262// will need sizeof(ImplType) earlier. In theory it's a tad slower because
263// the deleter is called by the pointer; however, the difference will not
264// be noticeable (if exist at all) for any class with non-trivial contents.
265
266//! \brief make_unique for ImplPtr
267//!
268//! Since std::make_unique is not compatible with ImplPtr, this should be used
269//! in constructors of frontend classes to create implementation instances.
270template <typename ImplType, typename TypeToDelete = ImplType, typename... ArgTs>
271inline ImplPtr<ImplType, TypeToDelete> makeImpl(ArgTs&&... args)
272{
273 return ImplPtr<ImplType, TypeToDelete> {
274 new ImplType{std::forward<ArgTs>(args)...},
275 [](TypeToDelete* impl) { delete impl; }
276 };
277}
278
279template <typename ImplType, typename TypeToDelete = ImplType>
280inline ImplPtr<ImplType, TypeToDelete> acquireImpl(ImplType* from)
281{
282 return ImplPtr<ImplType, TypeToDelete> { from, [](TypeToDelete* impl) {
283 delete impl;
284 } };
285}
286
287template <typename ImplType, typename TypeToDelete = ImplType>
289{
290 return { nullptr, [](TypeToDelete*) { /* nullptr doesn't need deletion */ } };
291}
292
293template <typename T>
295 size_t (*destructor)(T*);
296
297 void operator()(T* toDelete)
298 {
299 destructor(toDelete);
300 delete[] reinterpret_cast<std::byte*>(toDelete);
301 }
302};
303
304//! \brief An owning pointer to a C structure
305//!
306//! This is intented to ease lifecycle management of Olm structures.
307//! \sa makeCStruct
308template <typename T>
309using CStructPtr = std::unique_ptr<T, CStructDeleter<T>>;
310
311//! \brief Create a C structure with pre-programmed deletion logic
312//!
313//! This facility function creates a CStructPtr that owns the pointer returned
314//! by \p constructor. The memory passed to \p constructor is allocated
315//! as an array of bytes; the size of that array is determined by calling
316//! \p sizeFn. Finally, since the returned pointer is owning, it also stores
317//! the corresponding CStructDeleter instance; when called at destruction of
318//! the owning pointer, this deleter first calls \p destructor passing the
319//! original C pointer returned by \p constructor; and then deletes the
320//! allocated array of bytes.
321template <typename T>
322inline auto makeCStruct(T* (*constructor)(void*), size_t (*sizeFn)(),
323 auto destructor)
324{
325 return CStructPtr<T>{ constructor(new std::byte[sizeFn()]), { destructor } };
326}
327
328//! \brief Multiplex several functors in one
329//!
330//! This is a well-known trick to wrap several lambdas into a single functor
331//! class that can be passed to std::visit.
332//! \sa https://en.cppreference.com/w/cpp/utility/variant/visit
333template <typename... FunctorTs>
334struct Overloads : FunctorTs... {
335 using FunctorTs::operator()...;
336};
337
338template <typename... FunctorTs>
339Overloads(FunctorTs&&...) -> Overloads<FunctorTs...>;
340
341/** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */
342QUOTIENT_API void linkifyUrls(QString& htmlEscapedText);
343
344/** Sanitize the text before showing in HTML
345 *
346 * This does toHtmlEscaped() and removes Unicode BiDi marks.
347 */
349
350/** Pretty-print plain text into HTML
351 *
352 * This includes HTML escaping of <,>,",& and calling linkifyUrls()
353 */
355
356/** Return a path to cache directory after making sure that it exists
357 *
358 * The returned path has a trailing slash, clients don't need to append it.
359 * \param dirName path to cache directory relative to the standard cache path
360 */
362
363/** Hue color component of based of the hash of the string.
364 *
365 * The implementation is based on XEP-0392:
366 * https://xmpp.org/extensions/xep-0392.html
367 * Naming and range are the same as QColor's hueF method:
368 * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision
369 */
371
372/** Extract the serverpart from MXID */
374
379
380// QDebug manipulators
381
382//! \brief QDebug manipulator to setup the stream for JSON output
383//!
384//! Originally made to encapsulate the change in QDebug behavior in Qt 5.4
385//! and the respective addition of QDebug::noquote().
386//! Together with the operator<<() helper, the proposed usage is
387//! (similar to std:: I/O manipulators):
388//! `qCDebug(MAIN) << formatJson << json_object; // (QJsonObject etc.)`
389inline QDebug formatJson(QDebug dbg) { return dbg.noquote(); }
390
391//! Suppress full qualification of enums/QFlags when logging
392inline QDebug terse(QDebug dbg)
393{
394 return dbg.verbosity(QDebug::MinimumVerbosity);
395}
396
398#ifdef PROFILER_LOG_USECS
400#else
401 200
402#endif
403 * 1000;
404} // namespace Quotient
405
406//! \brief A helper operator for QDebug manipulators, e.g. formatJson
407//!
408//! \param dbg to output the json to
409//! \param manipFn a QDebug manipulator
410//! \return a copy of dbg that has its mode altered by manipFn
411inline QDebug operator<<(QDebug dbg, std::invocable<QDebug> auto manipFn)
412{
413 return std::invoke(manipFn, dbg);
414}
415
416inline QDebug operator<<(QDebug dbg, QElapsedTimer et)
417{
418 // NOLINTNEXTLINE(bugprone-integer-division)
419 dbg << static_cast<double>(et.nsecsElapsed() / 1000) / 1000
420 << "ms"; // Show in ms with 3 decimal digits precision
421 return dbg;
422}
423
424namespace Quotient {
425//! \brief Lift an operation into dereferenceable types (std::optional or pointers)
426//!
427//! This is a more generic version of std::optional::and_then() that accepts an arbitrary number of
428//! arguments of any type that is dereferenceable (i.e. unary operator*() can be applied to it) and
429//! (explicitly or implicitly) convertible to bool. This allows to streamline checking for
430//! nullptr/nullopt before applying the operation on the underlying types. \p fn is only invoked if
431//! all \p args are "truthy" (i.e. <tt>(... && bool(args)) == true</tt>).
432//! \param fn A callable that should accept the types stored inside optionals/pointers passed in
433//! \p args (NOT optionals/pointers themselves; they are unwrapped)
434//! \return Always an optional: if \p fn returns another type, lift() wraps it in std::optional;
435//! if \p fn returns std::optional, that return value (or std::nullopt) is returned as is.
436template <typename FnT>
437inline auto lift(FnT&& fn, auto&&... args)
438{
439 if constexpr (std::is_void_v<std::invoke_result_t<FnT, decltype(*args)...>>) {
440 if ((... && bool(args)))
441 std::invoke(std::forward<FnT>(fn), *args...);
442 } else
443 return (... && bool(args))
444 ? std::optional(std::invoke(std::forward<FnT>(fn), *args...))
445 : std::nullopt;
446}
447
448//! \brief Merge the value from an optional
449//!
450//! Assigns the value stored at \p rhs to \p lhs if, and only if, \p rhs is not omitted and
451//! `lhs != *rhs`. \p lhs can be either an optional or an ordinary variable.
452//! \return `true` if \p rhs is not omitted and the \p lhs value was different, in other words,
453//! if \p lhs has changed; `false` otherwise
454template <typename T1, typename T2>
455 requires std::is_assignable_v<T1&, const T2&>
456constexpr inline bool merge(T1& lhs, const std::optional<T2>& rhs)
457{
458 if (!rhs || lhs == *rhs)
459 return false;
460 lhs = *rhs;
461 return true;
462}
463
464//! \brief Merge structure-like types
465//!
466//! Merges fields in \p lhs from counterparts in \p rhs. The list of fields to merge is passed
467//! in additional parameters (\p fields). E.g.:
468//! \codeline mergeStruct(struct1, struct2, &Struct::field1, &Struct::field2, &Struct::field3)
469//! \return the number of fields in \p lhs that were changed
470template <typename StructT>
471constexpr inline size_t mergeStruct(StructT& lhs, const StructT& rhs, const auto... fields)
472{
473 return ((... + static_cast<size_t>(merge(lhs.*fields, rhs.*fields))));
474}
475
476// These are meant to eventually become separate classes derived from QString (or perhaps
477// QByteArray?), with their own construction and validation logic; for now they are just aliases
478// for QString to make numerous IDs at least semantically different in the code.
479
483
484QUOTIENT_API bool isGuestUserId(const UserId& uId);
485
490
492};
493} // namespace Quotient
constexpr iterator end()
Definition util.h:157
constexpr Range(iterator from, iterator to)
Definition util.h:146
constexpr bool empty() const
Definition util.h:153
constexpr const_iterator end() const
Definition util.h:155
constexpr const_iterator begin() const
Definition util.h:154
constexpr Range(ArrayT &arr)
Definition util.h:145
constexpr iterator begin()
Definition util.h:156
constexpr size_type size() const
Definition util.h:148
An adaptor for Qt (hash-)maps to make them iterable in STL style.
Definition util.h:185
QT_IGNORE_DEPRECATIONS(template< typename U > asKeyValueRange(U &) -> asKeyValueRange< U & >;template< typename U > asKeyValueRange(U &&) -> asKeyValueRange< U >;) template< typename InputIt
QUOTIENT_API bool isGuestUserId(const UserId &uId)
auto lift(FnT &&fn, auto &&... args)
Lift an operation into dereferenceable types (std::optional or pointers)
Definition util.h:437
auto makeReadyValueFuture(T &&value)
A substitute for QtFuture::makeReadyValueFuture() for compatibility with Qt pre-6....
Definition util.h:105
QUOTIENT_API int patchVersion()
QUOTIENT_API int minorVersion()
QDebug formatJson(QDebug dbg)
QDebug manipulator to setup the stream for JSON output.
Definition util.h:389
bool alarmX(bool alarmCondition, const auto &msg, std::source_location loc=std::source_location::current())
Definition util.h:65
Overloads(FunctorTs &&...) -> Overloads< FunctorTs... >
constexpr qint64 ProfilerMinNsecs
Definition util.h:397
constexpr ImplPtr< ImplType, TypeToDelete > ZeroImpl()
Definition util.h:288
QUOTIENT_API void linkifyUrls(QString &htmlEscapedText)
constexpr bool merge(T1 &lhs, const std::optional< T2 > &rhs)
Merge the value from an optional.
Definition util.h:456
constexpr size_t mergeStruct(StructT &lhs, const StructT &rhs, const auto... fields)
Merge structure-like types.
Definition util.h:471
ImplPtr< ImplType, TypeToDelete > makeImpl(ArgTs &&... args)
make_unique for ImplPtr
Definition util.h:271
QDebug terse(QDebug dbg)
Suppress full qualification of enums/QFlags when logging.
Definition util.h:392
constexpr auto operator""_ls(const char *s, std::size_t size)
Definition util.h:133
auto makeReadyVoidFuture()
A substitute for QtFuture::makeReadyVoidFuture() for compatibility with Qt pre-6.6.
Definition util.h:94
auto makeCStruct(T *(*constructor)(void *), size_t(*sizeFn)(), auto destructor)
Create a C structure with pre-programmed deletion logic.
Definition util.h:322
QUOTIENT_API int majorVersion()
ImplPtr< ImplType, TypeToDelete > acquireImpl(ImplType *from)
Definition util.h:280
#define QUOTIENT_API
void operator()(T *toDelete)
Definition util.h:297
This is only to make UnorderedMap alias work until we get rid of it.
Definition util.h:117
size_t operator()(const T &s) const Q_DECL_NOEXCEPT
Definition util.h:118
Multiplex several functors in one.
Definition util.h:334
QDebug operator<<(QDebug dbg, std::invocable< QDebug > auto manipFn)
A helper operator for QDebug manipulators, e.g. formatJson.
Definition util.h:411
#define QUO_CSTR(StringConvertible_)
q(Utf8)Printable that can handle more than just QStrings
Definition util.h:63