Коммит 2705ec3b создал по автору Ilya Pankratov's avatar Ilya Pankratov
Просмотр файлов

Implement Qt runtime

It helps to use QFuture as coroutine result
владелец 292161a3
# Based on: https://github.com/qt-creator/qt-creator/blob/master/.clang-format
---
Language: Cpp
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: None
AlignConsecutiveDeclarations: None
AlignEscapedNewlines: Left
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: true
AfterControlStatement: Never
AfterEnum: false
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: false
BeforeCatch: true
BeforeElse: false
BeforeLambdaBody: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 100
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: true
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: false
LambdaBodyIndentation: OuterScope
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PackConstructorInitializers: Never
PenaltyBreakAssignment: 150
PenaltyBreakBeforeFirstCallParameter: 300
PenaltyBreakComment: 500
PenaltyBreakFirstLessLess: 400
PenaltyBreakString: 600
PenaltyExcessCharacter: 50
PenaltyReturnTypeOnItsOwnLine: 300
PointerAlignment: Right
ReflowComments: false
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: c++17
TabWidth: 4
UseTab: Never
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "CallbackContext.hpp"
using CallbackContext = Aurora::Kmp::QtBindings::CallbackContext;
using OnResultCallback = Aurora::Kmp::QtBindings::OnResultCallback;
using OnErrorCallback = Aurora::Kmp::QtBindings::OnErrorCallback;
using OnCancelledCallback = Aurora::Kmp::QtBindings::OnCancelledCallback;
extern "C" {
void onResult(void *context, void *result)
{
auto callback = static_cast<CallbackContext *>(context)->onResultCallback;
callback(result);
}
void onError(void *context, char *errorMessage)
{
auto callback = static_cast<CallbackContext *>(context)->onErrorCallback;
callback(errorMessage);
}
void onCancelled(void *context, char *errorMessage)
{
auto callback = static_cast<CallbackContext *>(context)->onCancelledCallback;
callback(errorMessage);
}
}
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RU_AURORA_KMP_QT_BINDINGS_CALLBACK_CONTEXT_HPP
#define RU_AURORA_KMP_QT_BINDINGS_CALLBACK_CONTEXT_HPP
#include <functional>
extern "C" {
void onResult(void *context, void *result);
void onError(void *context, char *errorMessage);
void onCancelled(void *context, char *errorMessage);
}
namespace Aurora {
namespace Kmp {
namespace QtBindings {
using OnResultCallback = std::function<void(void * /* Result */)>;
using OnErrorCallback = std::function<void(char * /* String */)>;
using OnCancelledCallback = std::function<void(char * /* String */)>;
struct CallbackContext
{
OnResultCallback onResultCallback;
OnErrorCallback onErrorCallback;
OnCancelledCallback onCancelledCallback;
};
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
#endif /* RU_AURORA_KMP_QT_BINDINGS_CALLBACK_CONTEXT_HPP */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "CoroutineException.hpp"
namespace Aurora {
namespace Kmp {
namespace QtBindings {
CoroutineException::CoroutineException(const QString &message)
: m_message(message)
{}
CoroutineException::CoroutineException(const CoroutineException &other)
: QException(other)
, m_message(other.m_message)
{}
void CoroutineException::raise() const
{
throw *this;
}
CoroutineException *CoroutineException::clone() const
{
return new CoroutineException(*this);
}
QString CoroutineException::message() const
{
return m_message;
}
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RU_AURORA_KMP_QT_BINDINGS_EXCEPTION_HPP
#define RU_AURORA_KMP_QT_BINDINGS_EXCEPTION_HPP
#include <QException>
namespace Aurora {
namespace Kmp {
namespace QtBindings {
class CoroutineException: public QException
{
public:
explicit CoroutineException(const QString &message);
virtual ~CoroutineException() = default;
CoroutineException(const CoroutineException &other);
void raise() const override;
CoroutineException *clone() const override;
QString message() const;
private:
QString m_message;
};
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
#endif /* RU_AURORA_KMP_QT_BINDINGS_EXCEPTION_HPP */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "CoroutineLauncher.hpp"
namespace Aurora {
namespace Kmp {
namespace QtBindings {
CoroutineLauncher::CoroutineLauncher(KotlinCoroutineLauncher *launcher)
: d_ptr(launcher)
{}
CoroutineLauncher::CoroutineLauncher(CoroutineLauncher &&other)
{
d_ptr = other.d_ptr;
other.d_ptr = nullptr;
}
CoroutineLauncher::~CoroutineLauncher()
{
if (d_ptr) {
d_ptr->free(d_ptr);
}
}
CoroutineLauncher &CoroutineLauncher::operator=(CoroutineLauncher &&other)
{
if (this != &other) {
this->d_ptr = other.d_ptr;
other.d_ptr = nullptr;
return *this;
}
return *this;
}
CoroutineLauncher::CancelableCoroutine CoroutineLauncher::launch(CallbackContext *context,
COnResultCallback onResult,
COnErrorCallback onError,
COnCancelledCallback onCancelled)
{
return CancelableCoroutine{d_ptr->coroutineLauncherFunc((void *) context,
d_ptr->kotlinContext,
onResult,
onError,
onCancelled)};
}
CoroutineLauncher::CancelableCoroutine::CancelableCoroutine(CCancellableCoroutine *coroutine)
: d_ptr(coroutine)
{}
CoroutineLauncher::CancelableCoroutine::CancelableCoroutine(CancelableCoroutine &&other)
{
d_ptr = other.d_ptr;
other.d_ptr = nullptr;
}
CoroutineLauncher::CancelableCoroutine::~CancelableCoroutine()
{
if (d_ptr) {
d_ptr->free(d_ptr);
}
}
CoroutineLauncher::CancelableCoroutine &CoroutineLauncher::CancelableCoroutine::operator=(
CancelableCoroutine &&other)
{
if (this != &other) {
this->d_ptr = other.d_ptr;
other.d_ptr = nullptr;
return *this;
}
return *this;
}
void CoroutineLauncher::CancelableCoroutine::cancel()
{
d_ptr->cancel(d_ptr);
}
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RU_AURORA_KMP_QT_BINDINGS_COROUTINE_LAUNCHER_HPP
#define RU_AURORA_KMP_QT_BINDINGS_COROUTINE_LAUNCHER_HPP
#include "CallbackContext.hpp"
#include "cruntime.h"
namespace Aurora {
namespace Kmp {
namespace QtBindings {
struct CoroutineLauncher
{
CoroutineLauncher(KotlinCoroutineLauncher *launcher);
CoroutineLauncher(const CoroutineLauncher &other) = delete;
CoroutineLauncher(CoroutineLauncher &&other);
virtual ~CoroutineLauncher();
CoroutineLauncher &operator=(const CoroutineLauncher &other) = delete;
CoroutineLauncher &operator=(CoroutineLauncher &&other);
struct CancelableCoroutine
{
public:
CancelableCoroutine(CCancellableCoroutine *coroutine);
CancelableCoroutine(const CancelableCoroutine &other) = delete;
CancelableCoroutine(CancelableCoroutine &&other);
virtual ~CancelableCoroutine();
CancelableCoroutine &operator=(const CancelableCoroutine &other) = delete;
CancelableCoroutine &operator=(CancelableCoroutine &&other);
void cancel();
private:
CCancellableCoroutine *d_ptr;
};
CancelableCoroutine launch(CallbackContext *context,
COnResultCallback onResult,
COnErrorCallback onError,
COnCancelledCallback onCancelled);
private:
KotlinCoroutineLauncher *d_ptr;
};
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
#endif /* RU_AURORA_KMP_QT_BINDINGS_COROUTINE_LAUNCHER_HPP */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RU_AURORA_KMP_QT_BINDINGS_COROUTINE_OPERATION_HPP
#define RU_AURORA_KMP_QT_BINDINGS_COROUTINE_OPERATION_HPP
#include <functional>
#include <optional>
#include <QFuture>
#include <QFutureWatcher>
#include "CallbackContext.hpp"
#include "CoroutineException.hpp"
#include "CoroutineLauncher.hpp"
namespace Aurora {
namespace Kmp {
namespace QtBindings {
template<typename T>
using ResultTransformer = std::function<T(void *)>;
template<typename T>
std::enable_if_t<!std::is_void_v<T>, QFuture<T>> coroutine(CoroutineLauncher launcher,
const ResultTransformer<T> &transformer);
template<typename T>
std::enable_if_t<std::is_void_v<T>, QFuture<T>> coroutine(CoroutineLauncher launcher);
template<typename T>
class CoroutineOperation
{
public:
virtual ~CoroutineOperation() = default;
private:
friend QFuture<T> coroutine<T>(CoroutineLauncher, const ResultTransformer<T> &);
CoroutineOperation(CoroutineLauncher launcher)
: m_launcher(std::move(launcher))
, m_callbackContext{nullptr,
[this](char *message) { finishedWithError(QString::fromUtf8(message)); },
// TODO: Do we need to call m_futureInterface.reportCancelled()?
[](char *) {}}
{
auto watcher = new QFutureWatcher<T>();
watcher->setFuture(future());
QObject::connect(watcher, &QFutureWatcher<T>::canceled, [=, this]() {
if (m_cancellableCoroutine) {
m_cancellableCoroutine->cancel();
m_cancellableCoroutine.reset();
}
});
QObject::connect(watcher, &QFutureWatcher<T>::finished, [watcher]() {
watcher->deleteLater();
});
}
QFuture<T> launch(const ResultTransformer<T> &transformer)
{
m_callbackContext.onResultCallback = [this, transformer](void *result) {
auto transformedResult = transformer(result);
finishedWithResult(&transformedResult);
};
m_futureInterface.reportStarted();
m_cancellableCoroutine = m_launcher.launch(&m_callbackContext,
&onResult,
&onError,
&onCancelled);
return future();
}
void finishedWithResult(const T *result)
{
// If Qfuture has been cancelled, reportFinished will do nothing
m_futureInterface.reportFinished(result);
}
void finishedWithError(const QString &error)
{
// If Qfuture has been cancelled, reportException, reportFinished will do nothing
m_futureInterface.reportException(CoroutineException(error));
m_futureInterface.reportFinished();
}
QFuture<T> future()
{
return m_futureInterface.future();
}
CoroutineLauncher m_launcher;
std::optional<CoroutineLauncher::CancelableCoroutine> m_cancellableCoroutine;
CallbackContext m_callbackContext;
// We use the undocumented (but not explicitly marked as private) QFutureInterface class
// because that's the only way to manipulate a QFuture's status
QFutureInterface<T> m_futureInterface;
};
template<>
class CoroutineOperation<void>
{
public:
virtual ~CoroutineOperation() = default;
private:
friend QFuture<void> coroutine<void>(CoroutineLauncher launcher);
CoroutineOperation(CoroutineLauncher launcher)
: m_launcher(std::move(launcher))
, m_callbackContext{nullptr,
[this](char *message) { finishedWithError(QString::fromUtf8(message)); },
// TODO: Do we need to call m_futureInterface.reportCancelled()?
[](char *) {}}
{
auto watcher = new QFutureWatcher<void>();
watcher->setFuture(future());
QObject::connect(watcher, &QFutureWatcher<void>::canceled, [=]() {
if (m_cancellableCoroutine) {
m_cancellableCoroutine->cancel();
m_cancellableCoroutine.reset();
}
});
QObject::connect(watcher, &QFutureWatcher<void>::finished, [=]() {
watcher->deleteLater();
});
}
QFuture<void> launch()
{
m_callbackContext.onResultCallback = [this](void *) { finishedWithResult(); };
m_futureInterface.reportStarted();
m_cancellableCoroutine = m_launcher.launch(&m_callbackContext,
&onResult,
&onError,
&onCancelled);
return future();
}
void finishedWithResult()
{
// If Qfuture has been cancelled, reportFinished will do nothing
m_futureInterface.reportFinished();
}
void finishedWithError(const QString &error)
{
// If Qfuture has been cancelled, reportException, reportFinished will do nothing
m_futureInterface.reportException(CoroutineException(error));
m_futureInterface.reportFinished();
}
QFuture<void> future()
{
return m_futureInterface.future();
}
CoroutineLauncher m_launcher;
std::optional<CoroutineLauncher::CancelableCoroutine> m_cancellableCoroutine;
CallbackContext m_callbackContext;
// We use the undocumented (but not explicitly marked as private) QFutureInterface class
// because that's the only way to manipulate a QFuture's status
QFutureInterface<void> m_futureInterface;
};
template<typename T>
std::enable_if_t<!std::is_void_v<T>, QFuture<T>> coroutine(CoroutineLauncher launcher,
const ResultTransformer<T> &transformer)
{
auto op = new CoroutineOperation<T>(std::move(launcher));
auto watcher = new QFutureWatcher<T>();
watcher->setFuture(op->future());
QObject::connect(watcher, &QFutureWatcher<T>::finished, [=]() {
watcher->deleteLater();
delete op;
});
return op->launch(transformer);
}
template<typename T>
std::enable_if_t<std::is_void_v<T>, QFuture<T>> coroutine(CoroutineLauncher launcher)
{
auto op = new CoroutineOperation<T>(std::move(launcher));
auto watcher = new QFutureWatcher<T>();
watcher->setFuture(op->future());
QObject::connect(watcher, &QFutureWatcher<T>::finished, [=]() {
watcher->deleteLater();
delete op;
});
return op->launch();
}
} /* namespace Aurora */
} /* namespace Kmp */
} /* namespace QtBindings */
#endif /* RU_AURORA_KMP_QT_BINDINGS_COROUTINE_OPERATION_HPP */
/**
* SPDX-FileCopyrightText: Copyright 2025 Open Mobile Platform LLC <community@omp.ru>
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RU_AURORA_KMP_QT_BINDINGS_C_RUNTIME_H
#define RU_AURORA_KMP_QT_BINDINGS_C_RUNTIME_H
#ifdef __cplusplus
extern "C" {
#endif
struct CCancellableCoroutine;
typedef void (*COnResultCallback)(void *cContext, void *result);
typedef void (*COnStringCallback)(void *cContext, char *message);
typedef COnStringCallback COnErrorCallback;
typedef COnStringCallback COnCancelledCallback;
typedef void (*CCancelCoroutine)(struct CCancellableCoroutine *coroutine);
typedef struct CCancellableCoroutine
{
void *coroutine;
CCancelCoroutine cancel;
void (*free)(struct CCancellableCoroutine *thiz);
} CCancellableCoroutine;
typedef struct KotlinCoroutineLauncher
{
void *kotlinContext;
CCancellableCoroutine *(*coroutineLauncherFunc)(void *cContext,
void *kotlinContext,
COnResultCallback onResult,
COnErrorCallback onError,
COnCancelledCallback onCancelled);
void (*free)(struct KotlinCoroutineLauncher *thiz);
} KotlinCoroutineLauncher;
#ifdef __cplusplus
}
#endif
#endif /* RU_AURORA_KMP_QT_BINDINGS_C_RUNTIME_H */
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать