Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohoscommon_p.h
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QOHOSCOMMON_H
5#define QOHOSCOMMON_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtCore/private/qohoslogger_p.h>
19#include <QtCore/qglobal.h>
20#include <QtCore/qlogging.h>
21#include <QtCore/qdebug.h>
22#include <array>
23#include <cstdlib>
24#include <functional>
25#include <memory>
26#include <mutex>
27#include <optional>
28#include <string>
29#ifndef QT_NO_EXCEPTIONS
30#include <stdexcept>
31#endif
32#include <type_traits>
33
34#define Q_OHOS_NAMED_FUNC(func) (QT_PREPEND_NAMESPACE(makeQOhosNamedFunc)<decltype(func)*, func>)(QT_STRINGIFY(func))
35
36#define qOhosReportFatalErrorAndAbort(...)
37 do {
38 QT_PREPEND_NAMESPACE(QMessageLogger)(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).fatal(__VA_ARGS__);
39 std::abort();
40 } while (false)
41
42QT_BEGIN_NAMESPACE
43
44template<typename Func, typename... FuncArgs>
45using QOhosInvokeResult = decltype(std::declval<Func>()(std::declval<FuncArgs>()...));
46
47template<typename ...Ts>
48using QOhosConsumer = std::function<void(Ts...)>;
49
50template<typename T>
51using QOhosSupplier = std::function<T()>;
52
54
55template<typename>
57
58template<typename T>
59struct IsStdOptional<std::optional<T>> : std::true_type {};
60
61template<typename T>
63
64}
65
66template<typename T, typename Func>
67QOhosInvokeResult<Func, T> qAndThen(const std::optional<T> &opt, Func &&func);
68
69template<typename T, typename Func>
70std::optional<std::remove_cv_t<QOhosInvokeResult<Func, T>>> qTransform(const std::optional<T> &opt, Func &&func);
71
72template<typename... Args>
74{
75public:
76 template<typename F, typename = std::enable_if_t<!std::is_same<std::decay_t<F>, QOhosTaskPromise>::value>>
78 F &&callable, std::function<void()> optOnDestroyedWithoutCall,
79 std::string callerContextName = {});
80
82
85
88
89 void operator()(Args... args) const;
90
91 QOhosTaskPromise makeChained(std::string callerContextName) &&;
92
93 template<typename... ContextNames>
94 std::array<QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&;
95
96 std::pair<QOhosTaskPromise, QOhosTaskPromise> makeThenCatchBranches(std::string callerContextName) &&;
97
98 operator std::function<void(Args...)>() &&;
99
100private:
101 class Callable
102 {
103 public:
104 virtual ~Callable() = default;
105 virtual void call(Args &&...args) = 0;
106 };
107
108 template<typename Func>
109 class CallableImpl : public Callable
110 {
111 public:
112 template<typename F>
113 explicit CallableImpl(F &&f);
114
115 void call(Args &&...args) override;
116
117 private:
118 Func m_func;
119 };
120
121 struct TrackingUsageState
122 {
123 TrackingUsageState(
124 std::size_t activeCount,
125 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall);
126
127 std::shared_ptr<std::function<void()>> onDestroyedWithoutCall;
128 bool used = false;
129 std::size_t activeCount = 1;
130 };
131
132 struct TrackingNode
133 {
134 TrackingNode(
135 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
136 std::shared_ptr<TrackingUsageState> usage);
137
138 void markUsed(const char *callerContextString);
139 std::string rootCallerContextOrUnknown() const;
140 void logCallerContextChainAsErrors(const char *messagePrefix) const;
141
142 std::string callerContext;
143 std::shared_ptr<TrackingNode> optParent;
144 std::shared_ptr<TrackingUsageState> usage;
145 };
146
147 QOhosTaskPromise(std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking);
148
149 std::shared_ptr<Callable> m_callable;
150 std::shared_ptr<TrackingNode> m_tracking;
151};
152
153template<typename Func, Func func>
155{
156public:
157 static constexpr Func funcPtr = func;
158
159 QOhosNamedFunc(const char *funcName);
160
161 const char *name() const;
162 Func ptr() const;
163
164 operator Func () const;
165
166private:
167 static_assert(std::is_function_v<std::remove_pointer_t<Func>>);
168
169 const char *m_funcName;
170};
171
172template<typename Func, Func func>
174{
175 return {funcName};
176}
177
179{
180public:
181 template<typename ...Ts>
182 void operator()(const Ts &...) const;
183};
184
185template<typename ...Ts>
187
189
190template<typename T>
192{
193public:
195
198
201
202 template<typename ProcessFunc>
203 void processValue(ProcessFunc &&processFunc);
204
205 template<typename EvalFunc>
206 auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()));
207
208private:
209 mutable std::mutex m_valueMutex;
210 T m_value{};
211};
212
213namespace QtOhos {
214
215template<typename T>
216std::shared_ptr<T> moveToSharedPtr(T &&obj);
217
218template<typename T>
220 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData);
221
222template<typename T>
223std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj);
224
225std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy);
226
227}
228
229template<typename T, typename Func>
230QOhosInvokeResult<Func, T> qAndThen(const std::optional<T> &opt, Func &&func)
231{
232 static_assert(
233 qohoscommon_p_h_detail::isStdOptional<QOhosInvokeResult<Func, T>>,
234 "func must return a std::optional");
235 return opt.has_value() ? func(*opt) : QOhosInvokeResult<Func, T>();
236}
237
238template<typename T, typename Func>
239std::optional<std::remove_cv_t<QOhosInvokeResult<Func, T>>> qTransform(const std::optional<T> &opt, Func &&func)
240{
241 using TransformedT = std::remove_cv_t<QOhosInvokeResult<Func, T>>;
242 return opt.has_value()
243 ? std::optional<TransformedT>(func(*opt))
244 : std::optional<TransformedT>();
245}
246
247template<typename Func, Func func>
248QOhosNamedFunc<Func, func>::QOhosNamedFunc(const char *funcName)
249 : m_funcName(funcName)
250{
251}
252
253template<typename Func, Func func>
254const char *QOhosNamedFunc<Func, func>::name() const
255{
256 return m_funcName;
257}
258
259template<typename Func, Func func>
260Func QOhosNamedFunc<Func, func>::ptr() const
261{
262 return func;
263}
264
265template<typename Func, Func func>
266QOhosNamedFunc<Func, func>::operator Func () const
267{
268 return func;
269}
270
271template<typename ...Ts>
272void QOhosNoOpConsumer::operator()(const Ts &...) const
273{
274}
275
276template<typename ...Ts>
278{
279 return [](const Ts &...) {
280 };
281}
282
287
288template<typename T>
290
291template<typename T>
292template<typename ProcessFunc>
293void QOhosMutexProtectedValue<T>::processValue(ProcessFunc &&processFunc)
294{
295 std::lock_guard<std::mutex> valueLock(m_valueMutex);
296 std::forward<ProcessFunc>(processFunc)(m_value);
297}
298
299template<typename T>
300template<typename EvalFunc>
301auto QOhosMutexProtectedValue<T>::evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval<const T &>()))
302{
303 std::lock_guard<std::mutex> valueLock(m_valueMutex);
304 return std::forward<EvalFunc>(evalFunc)(m_value);
305}
306
307namespace QtOhos {
308
309template<typename T>
310std::shared_ptr<T> moveToSharedPtr(T &&obj)
311{
312 return std::make_shared<T>(std::forward<T>(obj));
313}
314
315template<typename T>
317 std::shared_ptr<T> baseSharedPtr, std::shared_ptr<void> extraData)
318{
319 auto *baseRawPtr = baseSharedPtr.get();
320 return std::shared_ptr<T>(
321 moveToSharedPtr(
322 std::make_pair(
323 std::move(baseSharedPtr),
324 std::move(extraData))),
325 baseRawPtr);
326}
327
328template<typename T>
329std::weak_ptr<T> makeWeakPtr(const std::shared_ptr<T> &obj)
330{
331 return obj;
332}
333
334inline std::shared_ptr<void> makeDestroyNotifier(std::function<void()> callOnDestroy)
335{
336 class DestroyNotifier
337 {
338 public:
339 explicit DestroyNotifier(std::function<void()> callOnDestroy)
340 : callOnDestroy(std::move(callOnDestroy))
341 {
342 }
343
344 DestroyNotifier(const DestroyNotifier &) = delete;
345 DestroyNotifier(DestroyNotifier &&) = delete;
346 DestroyNotifier &operator=(const DestroyNotifier &) = delete;
347 DestroyNotifier &operator=(DestroyNotifier &&) = delete;
348
349 ~DestroyNotifier()
350 {
351 callOnDestroy();
352 };
353
354 private:
355 std::function<void()> callOnDestroy;
356 };
357
358 return std::make_shared<DestroyNotifier>(std::move(callOnDestroy));
359}
360
361}
362
363template<typename... Args>
364template<typename F, typename>
366 F &&callable, std::function<void()> optOnDestroyedWithoutCall, std::string callerContextName)
368 , m_tracking(
369 std::make_shared<TrackingNode>(
370 std::move(callerContextName), nullptr,
371 std::make_shared<TrackingUsageState>(
372 1,
373 QtOhos::moveToSharedPtr(
374 optOnDestroyedWithoutCall
375 ? std::move(optOnDestroyedWithoutCall)
376 : makeQOhosNoOpConsumer<>()))))
377{
378}
379
380template<typename... Args>
382{
383 if (!m_tracking || m_tracking->usage->used)
384 return;
385
386 --m_tracking->usage->activeCount;
387
388 if (m_tracking->usage->activeCount == 0) {
389 m_tracking->logCallerContextChainAsErrors(Q_FUNC_INFO);
390 qOhosPrintfError(
391 "%s: promise destroyed without notifying the caller: %s",
392 Q_FUNC_INFO, m_tracking->rootCallerContextOrUnknown().c_str());
393 (*m_tracking->usage->onDestroyedWithoutCall)();
394 }
395}
396
397template<typename... Args>
398QOhosTaskPromise<Args...>::QOhosTaskPromise(
399 std::shared_ptr<Callable> callable, std::shared_ptr<TrackingNode> tracking)
400 : m_callable(std::move(callable))
401 , m_tracking(std::move(tracking))
402{
403}
404
405template<typename... Args>
406void QOhosTaskPromise<Args...>::operator()(Args... args) const
407{
408 if (!m_tracking)
409 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
410
411 if (!m_callable)
412 qOhosReportFatalErrorAndAbort("%s: called on empty QOhosTaskPromise", Q_FUNC_INFO);
413
414 m_tracking->markUsed(Q_FUNC_INFO);
415
416 m_callable->call(std::forward<Args>(args)...);
417}
418
419template<typename... Args>
420QOhosTaskPromise<Args...>::operator std::function<void(Args...)>() &&
421{
422 const auto funcInfo = Q_FUNC_INFO;
423
424 if (!m_tracking)
425 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", funcInfo);
426
427 if (!m_callable)
428 return {};
429
430 m_tracking->markUsed(funcInfo);
431
432 auto callable = std::move(m_callable);
433 auto tracking = std::move(m_tracking);
434
435 auto sharedCalledFlag = std::make_shared<bool>(false);
436 auto destroyNotifier = QtOhos::makeDestroyNotifier(
437 [sharedCalledFlag, tracking, funcInfo]() {
438 if (!*sharedCalledFlag) {
439 tracking->logCallerContextChainAsErrors(funcInfo);
440 qOhosPrintfError(
441 "%s: promise (as std::function) destroyed without notifying the caller: %s",
442 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
443 (*tracking->usage->onDestroyedWithoutCall)();
444 }
445 });
446
447 return [callable, sharedCalledFlag, tracking, funcInfo, destroyNotifier](Args... args) {
448 if (*sharedCalledFlag) {
450 "%s: promise (as std::function) called more than once for: %s",
451 funcInfo, tracking->rootCallerContextOrUnknown().c_str());
452 }
453 *sharedCalledFlag = true;
454 callable->call(std::forward<Args>(args)...);
455 };
456}
457
458template<typename... Args>
459QOhosTaskPromise<Args...> QOhosTaskPromise<Args...>::makeChained(std::string callerContextName) &&
460{
461 if (!m_tracking)
462 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
463
464 m_tracking->markUsed(Q_FUNC_INFO);
465
466 auto newUsage = std::make_shared<TrackingUsageState>(1, m_tracking->usage->onDestroyedWithoutCall);
467
468 return QOhosTaskPromise(
469 std::move(m_callable),
470 std::make_shared<TrackingNode>(
471 std::move(callerContextName), std::move(m_tracking), newUsage));
472}
473
474template<typename... Args>
475template<typename... ContextNames>
476std::array<QOhosTaskPromise<Args...>, sizeof...(ContextNames)>
477QOhosTaskPromise<Args...>::makeBranched(ContextNames &&...branchContextNames) &&
478{
479 if (!m_tracking)
480 qOhosReportFatalErrorAndAbort("%s: called on moved-from instance", Q_FUNC_INFO);
481
482 m_tracking->markUsed(Q_FUNC_INFO);
483
484 auto sharedUsage = std::make_shared<TrackingUsageState>(
485 sizeof...(ContextNames), m_tracking->usage->onDestroyedWithoutCall);
486 return {{
488 m_callable,
489 std::make_shared<TrackingNode>(
490 std::forward<ContextNames>(branchContextNames),
491 m_tracking, sharedUsage))...
492 }};
493}
494
495template<typename... Args>
496std::pair<QOhosTaskPromise<Args...>, QOhosTaskPromise<Args...>>
497QOhosTaskPromise<Args...>::makeThenCatchBranches(std::string callerContextName) &&
498{
499 auto branches = std::move(*this).makeChained(std::move(callerContextName)).makeBranched("then", "catch");
500 return {std::move(branches[0]), std::move(branches[1])};
501}
502
503template<typename... Args>
504template<typename Func>
505template<typename F>
506QOhosTaskPromise<Args...>::CallableImpl<Func>::CallableImpl(F &&f)
507 : m_func(std::forward<F>(f))
508{
509}
510
511template<typename... Args>
512template<typename Func>
513void QOhosTaskPromise<Args...>::CallableImpl<Func>::call(Args &&...args)
514{
515 m_func(std::forward<Args>(args)...);
516}
517
518template<typename... Args>
519QOhosTaskPromise<Args...>::TrackingUsageState::TrackingUsageState(
520 std::size_t activeCount, std::shared_ptr<std::function<void()>> onDestroyedWithoutCall)
521 : onDestroyedWithoutCall(onDestroyedWithoutCall)
522 , activeCount(activeCount)
523{
524}
525
526template<typename... Args>
527QOhosTaskPromise<Args...>::TrackingNode::TrackingNode(
528 std::string callerContext, std::shared_ptr<TrackingNode> optParent,
529 std::shared_ptr<TrackingUsageState> usage)
530 : callerContext(std::move(callerContext))
531 , optParent(std::move(optParent))
532 , usage(std::move(usage))
533{
534}
535
536template<typename... Args>
537void QOhosTaskPromise<Args...>::TrackingNode::markUsed(const char *callerContextString)
538{
539 if (usage->used) {
540 logCallerContextChainAsErrors(callerContextString);
542 "%s: using a dead promise branch (%s) from this caller: %s",
543 callerContextString, callerContext.c_str(), rootCallerContextOrUnknown().c_str());
544 }
545 usage->used = true;
546}
547
548template<typename... Args>
549std::string QOhosTaskPromise<Args...>::TrackingNode::rootCallerContextOrUnknown() const
550{
551 const TrackingNode *rootContextNode = nullptr;
552 for (auto currentNode = this; currentNode != nullptr; currentNode = currentNode->optParent.get()) {
553 if (!currentNode->callerContext.empty())
554 rootContextNode = currentNode;
555 }
556 return rootContextNode != nullptr ? rootContextNode->callerContext : "<unknown>";
557}
558
559template<typename... Args>
560void QOhosTaskPromise<Args...>::TrackingNode::logCallerContextChainAsErrors(const char *messagePrefix) const
561{
562 int index = 0;
563 for (auto currentNode = this; currentNode; currentNode = currentNode->optParent.get()) {
564 if (!currentNode->callerContext.empty()) {
565 qOhosPrintfError(
566 "%s [caller context #%d]: %s",
567 messagePrefix, index, currentNode->callerContext.c_str());
568 ++index;
569 }
570 }
571}
572
573QT_END_NAMESPACE
574
575#endif
auto evalWithValue(EvalFunc &&evalFunc) const -> decltype(evalFunc(std::declval< const T & >()))
QOhosMutexProtectedValue & operator=(const QOhosMutexProtectedValue< T > &other)=delete
void processValue(ProcessFunc &&processFunc)
QOhosMutexProtectedValue & operator=(QOhosMutexProtectedValue< T > &&other)=delete
QOhosMutexProtectedValue(const QOhosMutexProtectedValue< T > &other)=delete
QOhosMutexProtectedValue(QOhosMutexProtectedValue< T > &&other)=delete
operator Func() const
Func ptr() const
QOhosNamedFunc(const char *funcName)
const char * name() const
static constexpr Func funcPtr
void operator()(const Ts &...) const
std::array< QOhosTaskPromise, sizeof...(ContextNames)> makeBranched(ContextNames &&...branchContextNames) &&
void operator()(Args... args) const
QOhosTaskPromise(F &&callable, std::function< void()> optOnDestroyedWithoutCall, std::string callerContextName={})
QOhosTaskPromise makeChained(std::string callerContextName) &&
operator std::function< void(Args...)>() &&
QOhosTaskPromise & operator=(const QOhosTaskPromise &)=delete
QOhosTaskPromise & operator=(QOhosTaskPromise &&)=default
std::pair< QOhosTaskPromise, QOhosTaskPromise > makeThenCatchBranches(std::string callerContextName) &&
QOhosTaskPromise(const QOhosTaskPromise &)=delete
QOhosTaskPromise(QOhosTaskPromise &&)=default
std::shared_ptr< T > makeSharedPtrWithAttachedExtraData(std::shared_ptr< T > baseSharedPtr, std::shared_ptr< void > extraData)
std::shared_ptr< T > moveToSharedPtr(T &&obj)
std::weak_ptr< T > makeWeakPtr(const std::shared_ptr< T > &obj)
std::shared_ptr< void > makeDestroyNotifier(std::function< void()> callOnDestroy)
constexpr bool isStdOptional
QOhosInvokeResult< Func, T > qAndThen(const std::optional< T > &opt, Func &&func)
#define qOhosReportFatalErrorAndAbort(...)
std::optional< std::remove_cv_t< QOhosInvokeResult< Func, T > > > qTransform(const std::optional< T > &opt, Func &&func)
QOhosNoOpConsumer makeQOhosNoOpConsumer()
std::function< T()> QOhosSupplier
std::function< void(Ts...)> QOhosConsumer
QOhosConsumer< Ts... > makeQOhosNoOpConsumer()
QOhosNamedFunc< Func, func > makeQOhosNamedFunc(const char *funcName)