# Hello, QtBindings!

**[<span class="icon material-symbols-outlined">link</span>mos.hub](https://hub.mos.ru/auroraos/kotlin-multiplatform/demos/demo-kmp)**

> В этом разделе описан процесс создания приложения с использованием
> **QtBindings** и нативного для Linux таргета (_linuxX64_ и _linuxArm64_) для ОС Аврора.
>
> Уже установили необходимые зависимости в [Maven Local](./connection.md)?
> Время написать первое приложение на KMP для ОС Аврора!

### Структура

#### [Часть 1: Проект Kotlin Multiplatform](#1-Проект-kotlin-multiplatform)

Подготовка библиотеки Multiplatform с общей бизнес логикой проекта.

#### [Часть 2: Проект ОС Аврора](#2-Проект-ОС-Аврора)

Подготовка приложения для ОС Аврора на Qt/Qml.

#### [Часть 3: Объединение проектов](#3-Объединение-проектов)

Добавление QtBindings в библиотеку Multiplatform и интеграция с приложением ОС Аврора.

#### [Часть 4: Выполнение метода](#4-Выполнение-метода)

Выполнение синхронного метода из библиотеки Multiplatform с использованием QtBindings.

#### [Часть 5: Использование Coroutines](#5-Использование-coroutines)

Использование QtBindings для выполнения [suspend](https://kotlinlang.org/docs/composing-suspending-functions.html) функций.

### 1. Проект Kotlin Multiplatform

Для создания библиотеки Kotlin Multiplatform можно воспользоваться сайтом [Kotlin Multiplatform Wizard](https://kmp.jetbrains.com/).
Необходимый шаблон библиотеки называется **Multiplatform Library**:

![preview](../../assets/images/qt-bindings/hw1.png)

По умолчанию в шаблоне **Multiplatform Library** добавлены все доступные цели сборки.
Для реализации библиотеки под проект ОС Аврора необходимо оставить только цель `linuxX64Main`.
Всё лишнее можно удалить.

После правок структура проекта должна выглядеть так:

```shell
.
├── build.gradle.kts
├── .gitignore
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library
│   ├── build.gradle.kts
│   └── src
│       ├── commonMain
│       │   └── kotlin
│       │       └── CustomFibi.kt
│       └── linuxX64Main
│           └── kotlin
│               └── fibiprops.linuxX64.kt
├── README.md
└── settings.gradle.kts

9 directories, 13 files
```

Название библиотеки будет часто фигурировать в проекте.
Для удобства его можно поместить в `library/gradle.properties`:

```properties
#Library
libName=demo_kmp
```

На данный момент в шаблоне есть цель `linuxX64`, для поддержки архитектуры `aarch64` необходимо добавить цель `linuxArm64`.
В названиях целей можно обозначить задачу создания библиотеки Kotlin/Native для ОС Аврора.

```kotlin
plugins {
    alias(libs.plugins.kotlinMultiplatform)
}

group = "ru.auroraos.demo"
version = "0.0.1"

kotlin {
    linuxX64("auroraX64") {
        binaries {
            staticLib {
                baseName = findProperty("libName").toString()
            }
        }
    }
    linuxArm64("auroraArm64") {
        binaries {
            staticLib {
                baseName = findProperty("libName").toString()
            }
        }
    }
    sourceSets {
        val commonMain by getting {
            dependencies {
                //put your multiplatform dependencies here
            }
        }
    }
}
```

Необходимо создать директории `auroraArm64Main` и `auroraX64Main` из существующего шаблона `linuxX64Main`.

> Обратите внимание на директории и названия файлов в них.

После правок целей структура проекта должна выглядеть так:

```shell
.
├── build.gradle.kts
├── .gitignore
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library
│   ├── build.gradle.kts
│   └── src
│       ├── auroraArm64Main
│       │   └── kotlin
│       │       └── fibiprops.auroraArm64.kt
│       ├── auroraX64Main
│       │   └── kotlin
│       │       └── fibiprops.auroraX64.kt
│       └── commonMain
│           └── kotlin
│               └── CustomFibi.kt
├── README.md
└── settings.gradle.kts

11 directories, 14 files
```

Чтобы убедиться, что все правки внесены верно, можно собрать проект:

```shell
./gradlew linkReleaseStaticAuroraX64 linkReleaseStaticAuroraX64
```

В конце лога сборки должно быть выведено сообщение: `BUILD SUCCESSFUL`.

Шаблон под разработку библиотеки с бизнес-логикой приложения для ОС Аврора готов.
Позже будет добавлено все необходимое для того, чтобы выполнять функции из библиотеки в приложении.
Но сначала необходимо создать приложение.

### 2. Проект ОС Аврора

Аврора IDE,
входящая в состав [Аврора SDK](https://developer.auroraos.ru/doc/sdk),
позволяет создать приложение из доступных шаблонов.
Для этого необходимо открыть Аврора IDE, нажать **New** в разделе **Welcome → Projects**.

![preview](../../assets/images/qt-bindings/hw2.png)

После конфигурации проекта структура проекта должна выглядеть так:

```shell
.
├── icons
│   ├── 108x108
│   │   └── ru.auroraos.demo_kmp.png
│   ├── 128x128
│   │   └── ru.auroraos.demo_kmp.png
│   ├── 172x172
│   │   └── ru.auroraos.demo_kmp.png
│   └── 86x86
│       └── ru.auroraos.demo_kmp.png
├── qml
│   ├── cover
│   │   └── DefaultCoverPage.qml
│   ├── demo_kmp.qml
│   ├── icons
│   │   └── demo_kmp.svg
│   └── pages
│       ├── AboutPage.qml
│       └── MainPage.qml
├── rpm
│   └── ru.auroraos.demo_kmp.spec
├── ru.auroraos.demo_kmp.desktop
├── ru.auroraos.demo_kmp.pro
├── src
│   └── main.cpp
└── translations
    ├── ru.auroraos.demo_kmp-ru.ts
    └── ru.auroraos.demo_kmp.ts

13 directories, 15 files
```

Собрать проект можно, нажав 🔨 (слева внизу).

![preview](../../assets/images/qt-bindings/hw3.png)

### 3. Объединение проектов

#### Публикация QtBindings

Для использования QtBindings необходимо использовать [Maven Local Repositories](https://maven.apache.org/repositories/local.html).

Для сборки и публикации плагина QtBindings в `Maven Local` нужно клонировать открытый проект из [mos.hub](https://hub.mos.ru/auroraos/kotlin-multiplatform/qt-bindings):

```shell
git clone https://hub.mos.ru/auroraos/kotlin-multiplatform/qt-bindings.git
```

Затем перейти в директорию с проектом и выполнить команду:

```shell
./gradlew publishToMavenLocal
```

Эта команда соберет проект и опубликует его в `Maven Local`,
после чего плагин станет доступен на персональном компьютере разработчика.

#### Подготовка библиотеки Multiplatform

Kotlin Multiplatform для целей Linux позволяет собрать динамическую или статическую библиотеку, которую можно использовать в Qt.
Статическая библиотека более удобна в использовании, поэтому в примере используется именно она.

К проекту библиотеки Kotlin/Native необходимо подключить QtBindings.
Он был добавлен в `Maven Local`, который необходимо включить в проекте.
В файл `settings.gradle.kts` допишем методы `mavenLocal()`:

```kotlin
pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
        mavenLocal()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        mavenLocal()
    }
}
```

В файл `gradle/libs.versions.toml` добавим плагин QtBindings и [KSP](https://kotlinlang.org/docs/ksp-overview.html):

```toml
[versions]
kotlin = "2.1.10"
qtbindings = "0.1.0"
devtoolsKsp = "2.1.10-1.0.31"

[plugins]
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
qtBindings = { id = "ru.auroraos.kmp.qtbindings", version.ref = "qtbindings" }
devtoolsKsp = { id = "com.google.devtools.ksp", version.ref = "devtoolsKsp" }
```

Добавим плагины в `build.gradle.kts`:

```kotlin
plugins {
    alias(libs.plugins.kotlinMultiplatform) apply false
    alias(libs.plugins.qtBindings) apply false
    alias(libs.plugins.devtoolsKsp) apply false
}
```

Добавим плагины в `library/build.gradle.kts`:

```kotlin
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.qtBindings)
    alias(libs.plugins.devtoolsKsp)
}
```

Плагину QtBindings необходимо указать название библиотеки.
Название должно соответствовать названию библиотеки, указанному в целях `auroraArm64Main` и `auroraX64Main`.

```kotlin
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.qtBindings)
    alias(libs.plugins.devtoolsKsp)
}

qtBindings {
    libName = findProperty("libName").toString()
}
```

#### Аннотация @QtExport

В демонстрационной функции шаблона библиотеки используется метод с `sequence`:

```kotlin
fun generateFibi() = sequence {
    var a = firstElement
    yield(a)
    var b = secondElement
    yield(b)
    while (true) {
        val c = a + b
        yield(c)
        a = b
        b = c
    }
}
```

Этот функционал на данный момент не поддерживается,
можно добавить промежуточную функцию `generateFibiWrapper` и включить её в экспорт:

```kotlin
import ru.auroraos.kmp.qtbindings.QtExport

@QtExport
fun generateFibiWrapper(take: Int): List<Int> {
    return generateFibi().take(take).toList()
}
```

> Более подробно с информацией о поддержке можно ознакомиться [здесь](./support.md).

Всё необходимое для генерации привязок имеется, запустим сборку библиотеки Kotlin/Native:

```shell
./gradlew linkReleaseStaticAuroraX64 linkReleaseStaticAuroraArm64
```

После добавления QtBindings и сборки будут созданы С++ файлы, которые можно добавить к проекту для ОС Аврора:

*	`library/build/generated/ksp/auroraArm64/auroraArm64Main/resources/*`
*	`library/build/generated/ksp/auroraX64/auroraX64Main/resources/*`

```shell
.
├── io
│   └── github
│       └── kotlin
│           └── fibonacci
│               ├── CustomFibi.cpp
│               └── CustomFibi.hpp
└── ru
    └── aurora
        └── kmp
            └── qtbindings
                ├── CallbackContext.cpp
                ├── CallbackContext.hpp
                ├── CoroutineException.cpp
                ├── CoroutineException.hpp
                ├── CoroutineLauncher.cpp
                ├── CoroutineLauncher.hpp
                ├── CoroutineOperation.hpp
                └── cruntime.h

9 directories, 10 files
```

#### Подготовка приложения ОС Аврора

После сборки проекта библиотеки Kotlin/Native, QtBindings создаст файлы C++ `*.hpp` и `*.cpp`.
Их нужно добавить напрямую в проект, но удобнее отделить их от основного проекта.
Для этого в проекте можно сделать субдиректорию.

В корне проекта ОС Аврора нужно:

1.	Создать директорию `ru.auroraos.demo_kmp` для основного приложения.
2.	Создать директорию `kmp.bindings` для сгенерированных Qt-привязок.
3.	Создать директорию `kmp.libs` для C-библиотек Kotlin Multiplatform.

Далее необходимо перенести основной проект ОС Аврора в директорию `ru.auroraos.demo_kmp`.
Структура проекта примет следующий вид:

```shell
.
├── kmp.bindings
├── kmp.libs
├── rpm
│   └── ru.auroraos.demo_kmp.spec
└── ru.auroraos.demo_kmp
    ├── icons
    │   ├── 108x108
    │   │   └── ru.auroraos.demo_kmp.png
    │   ├── 128x128
    │   │   └── ru.auroraos.demo_kmp.png
    │   ├── 172x172
    │   │   └── ru.auroraos.demo_kmp.png
    │   └── 86x86
    │       └── ru.auroraos.demo_kmp.png
    ├── qml
    │   ├── cover
    │   │   └── DefaultCoverPage.qml
    │   ├── demo_kmp.qml
    │   ├── icons
    │   │   └── demo_kmp.svg
    │   └── pages
    │       ├── AboutPage.qml
    │       └── MainPage.qml
    ├── ru.auroraos.demo_kmp.desktop
    ├── ru.auroraos.demo_kmp.pro
    ├── src
    │   └── main.cpp
    └── translations
        ├── ru.auroraos.demo_kmp-ru.ts
        └── ru.auroraos.demo_kmp.ts

16 directories, 16 files
```

В корень проекта ОС Аврора необходимо добавить файл `demo_kmp.pro` со следующим содержимым:

```pro
TEMPLATE = subdirs

OTHER_FILES += $$files(rpm/*)

SUBDIRS += \
    kmp.bindings \
    ru.auroraos.demo_kmp \

ru.auroraos.demo_kmp.depends = kmp.bindings

CONFIG += ordered
```

Затем в Аврора IDE можно открыть файл `demo_kmp.pro` и указать необходимые цели сборки:

![preview](../../assets/images/qt-bindings/hw4.png)

#### Объединение проектов

После любых модификаций библиотеки Kotlin/Native следует её сборка,
что, вероятно, изменит файлы, генерируемые QtBindings.
Для того, чтобы не переносить изменения самостоятельно,
можно автоматизировать этот процесс через Gradle-задачи.

Чтобы получить относительные пути копирования необходимых файлов,
можно совместить проект библиотеки Multiplatform и приложения для ОС Аврора:

```shell
.
├── auroraApp
│   ├── demo_kmp.pro
│   ├── kmp.bindings
│   ├── kmp.libs
│   ├── rpm
│   │   └── ru.auroraos.demo_kmp.spec
│   └── ru.auroraos.demo_kmp
│       ├── icons
│       │   ├── 108x108
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   ├── 128x128
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   ├── 172x172
│       │   │   └── ru.auroraos.demo_kmp.png
│       │   └── 86x86
│       │       └── ru.auroraos.demo_kmp.png
│       ├── qml
│       │   ├── cover
│       │   │   └── DefaultCoverPage.qml
│       │   ├── demo_kmp.qml
│       │   ├── icons
│       │   │   └── demo_kmp.svg
│       │   └── pages
│       │       ├── AboutPage.qml
│       │       └── MainPage.qml
│       ├── ru.auroraos.demo_kmp.desktop
│       ├── ru.auroraos.demo_kmp.pro
│       ├── src
│       │   └── main.cpp
│       └── translations
│           ├── ru.auroraos.demo_kmp-ru.ts
│           └── ru.auroraos.demo_kmp.ts
└── library
    ├── build.gradle.kts
    ├── gradle
    │   ├── libs.versions.toml
    │   └── wrapper
    │       ├── gradle-wrapper.jar
    │       └── gradle-wrapper.properties
    ├── gradle.properties
    ├── gradlew
    ├── gradlew.bat
    ├── library
    │   ├── build.gradle.kts
    │   └── src
    │       ├── auroraArm64Main
    │       │   └── kotlin
    │       │       └── fibiprops.auroraArm64.kt
    │       ├── auroraX64Main
    │       │   └── kotlin
    │       │       └── fibiprops.auroraX64.kt
    │       └── commonMain
    │           └── kotlin
    │               └── CustomFibi.kt
    ├── README.md
    └── settings.gradle.kts

28 directories, 28 files
```

#### Добавление задачи Gradle

Для автоматизации процесса обновления приложения ОС Аврора,
после сборки проекта библиотеки Kotlin/Native
зарегистрируем задачу.
Эта задача выполнит следующее:

1.	Соберёт статические C-библиотеки для архитектур x86_64 и aarch64.
2.	Скопирует собранные библиотеки в проект ОС Аврора.
3.	Скопирует привязки QtBindings в проект ОС Аврора.
4.	Создаст pro-файл для компиляции Qt-привязок в приложении ОС Аврора.
5.	Выведет информацию по подключению библиотек к приложению ОС Аврора.

```kotlin
interface Injected {
    @get:Inject val fs: FileSystemOperations
    @get:Inject val layout: ProjectLayout
    @get:Inject val objects: ObjectFactory
}

tasks.register("buildAuroraLibs") {
    // Injected
    val libName = findProperty("libName").toString()
    val injected = project.objects.newInstance<Injected>()
    // Task depends
    dependsOn("linkReleaseStaticAuroraX64", "linkReleaseStaticAuroraArm64")
    // Copy kmp lib x64
    doLast {
        injected.fs.delete {
            delete("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/x64")
        }
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("bin/auroraX64/releaseStatic")) { include("**/*.a", "**/*.h") }
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/x64")
        }
    }
    // Copy kmp lib arm64
    doLast {
        injected.fs.delete {
            delete("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/arm64")
        }
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("bin/auroraArm64/releaseStatic")) { include("**/*.a", "**/*.h") }
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.libs/arm64")
        }
    }
    // Copy kmp bindings
    // The target is not important where we will get the bindings from
    doLast {
        injected.fs.copy {
            from(injected.layout.buildDirectory.dir("generated/ksp/auroraX64/auroraX64Main/resources"))
            into("${injected.layout.projectDirectory}/../../auroraApp/kmp.bindings")
        }
    }
    // Gen pro bindings
    doLast {
        val search = injected.layout.buildDirectory.dir("generated/ksp/auroraX64/auroraX64Main/resources")
        val headers = mutableListOf<String>()
        val sources = mutableListOf<String>()
        val includes = mutableListOf<String>()
        injected.objects.fileTree().from(search).forEach {
            when {
                it.path.endsWith(".h") || it.path.endsWith(".hpp") -> {
                    headers.add(it.path.replace("${search.get().asFile}/", ""))
                }

                it.path.endsWith(".cpp") -> {
                    sources.add(it.path.replace("${search.get().asFile}/", ""))
                }
            }
        }
        headers.forEach {
            includes.add(it.replaceAfterLast("/", ""))
        }
        val file = File("${injected.layout.projectDirectory}/../../auroraApp/kmp.bindings/kmp.bindings.pro")
        file.writeText("""
            |TEMPLATE = lib
            |TARGET = kmpbindings
            |CONFIG += staticlib warn_off
            |
            |HEADERS += \
            |${headers.joinToString("\n") { "    $it \\" }}
            |
            |SOURCES += \
            |${sources.joinToString("\n") { "    $it \\" }}
            |
            |contains(QMAKE_HOST.arch, armv7l): {
            |    error("Unsupported architecture armv7l")
            |}
            |contains(QMAKE_HOST.arch, aarch64): {
            |    LIB_DEMO_KMP=kmp.libs/arm64
            |}
            |contains(QMAKE_HOST.arch, x86_64): {
            |    LIB_DEMO_KMP=kmp.libs/x64
            |}
            |
            |INCLUDEPATH += $${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP}
            |${includes.distinct().joinToString("\n") { "INCLUDEPATH += $${'$'}PWD/$it" }}
        """.trimMargin())
        file.createNewFile()
        // Print what needs to be added to the pro file of the application
        println("""
            |Check your application pro file:
            |################################
            |# kmp.targets
            |contains(QMAKE_HOST.arch, armv7l): {
            |    error("Unsupported architecture armv7l")
            |}
            |contains(QMAKE_HOST.arch, aarch64): {
            |    LIB_DEMO_KMP=kmp.libs/arm64
            |}
            |contains(QMAKE_HOST.arch, x86_64): {
            |    LIB_DEMO_KMP=kmp.libs/x64
            |}
            |
            |# kmp.bindings
            |${includes.distinct().joinToString("\n") { "INCLUDEPATH += $${'$'}PWD/../kmp.bindings/$it" }}
            |LIBS += -L${'$'}${'$'}OUT_PWD/../kmp.bindings -lkmpbindings
            |
            |# kmp.libs
            |INCLUDEPATH += ${'$'}${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP}
            |LIBS += -L${'$'}${'$'}PWD/../${'$'}${'$'}{LIB_DEMO_KMP} -l${libName}
            |################################
        """.trimMargin())
    }
}
```

> В задаче `buildAuroraLibs` привязки копируются с учётом их идентичности для целей, но это может быть не так.
> Если в вашем приложении они отличаются, следует доработать задачу согласно логике вашего проекта.

Вывод задачи Gradle:

```
# kmp.targets
contains(QMAKE_HOST.arch, armv7l): {
    error("Unsupported architecture armv7l")
}
contains(QMAKE_HOST.arch, x86_64): {
    LIB_DEMO_KMP=kmp.libs/x64
}
contains(QMAKE_HOST.arch, aarch64): {
    LIB_DEMO_KMP=kmp.libs/arm64
}

# kmp.bindings
INCLUDEPATH += $$PWD/../kmp.bindings/ru/aurora/kmp/qtbindings/
INCLUDEPATH += $$PWD/../kmp.bindings/io/github/kotlin/fibonacci/
LIBS += -L$$OUT_PWD/../kmp.bindings -lkmpbindings

# kmp.libs
INCLUDEPATH += $$PWD/../$${LIB_DEMO_KMP}
LIBS += -L$$PWD/../$${LIB_DEMO_KMP} -ldemo_kmp
```

Вывод из Gradle следует добавить в конец файла `auroraApp/ru.auroraos.demo_kmp/ru.auroraos.demo_kmp.pro` проекта ОС Аврора.

Теперь приложение ОС Аврора можно собрать:

![preview](../../assets/images/qt-bindings/hw5.png)

### 4. Выполнение метода

На данный момент общий проект содержит:

1.	Библиотеку Kotlin/Native.
2.	Приложение Qt/Qml для ОС Аврора.
3.	Задачу `buildAuroraLibs`, автоматизирующую процесс сборки и синхронизации.
4.	Статическую библиотеку с файлами генерации QtBindings, интегрированную с приложением.
5.	Метод в библиотеке Kotlin/Native с аннотацией `@QtExport`, позволяющий выполнить его в приложении.

Метод `generateFibiWrapper` с аннотацией `@QtExport` помещён в пространство имён соответственно пакету:

```cpp
namespace io {
namespace github {
namespace kotlin {
namespace fibonacci {

QList<int> generateFibiWrapper();

} /* namespace fibonacci */
} /* namespace kotlin */
} /* namespace github */
} /* namespace io */
```

В шаблоне приложения ОС Аврора можно найти основную функцию С++ — `main`.
В ней можно проверить результат работы QtBindings и библиотеки Kotlin/Native:

```cpp
#include <auroraapp.h>
#include <QtQuick>
#include <QDebug>

#include "CustomFibi.hpp"

int main(int argc, char *argv[])
{
    // Run from library Kotlin/Native
    qDebug() << io::github::kotlin::fibonacci::generateFibiWrapper(5);

    // Qt/Qml application
    QScopedPointer<QGuiApplication> application(Aurora::Application::application(argc, argv));
    application->setOrganizationName(QStringLiteral("ru.auroraos"));
    application->setApplicationName(QStringLiteral("demo_kmp"));

    QScopedPointer<QQuickView> view(Aurora::Application::createView());
    view->setSource(Aurora::Application::pathTo(QStringLiteral("qml/demo_kmp.qml")));
    view->show();

    return application->exec();
}
```

После запуска приложения в логах можно наблюдать выполнение функции `generateFibiWrapper` и привязок QtBindings из библиотеки Kotlin/Native:

![preview](../../assets/images/qt-bindings/hw6.png)

### 5. Использование Coroutines

QtBindings позволяет работать с Kotlin [Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) через Qt:

*	ожидать завершение `suspend`-функции;
*	отменять его при необходимости;
*	получать исключения.

В библиотеке Kotlin/Native есть синхронная функция `generateFibiWrapper`, которая была вызвана ранее.
Для демонстрации работы с асинхронными функциями добавим `suspend`-функцию:

```kotlin
@QtExport
suspend fun generateFibiWrapperAsync(take: Int): List<Int> {
    delay(5000L)
    return generateFibi().take(take).toList()
}
```

После добавления функции в библиотеку Kotlin/Native её необходимо собрать и обновить файлы, генерируемые QtBindings в проекте ОС Аврора.
Сделать это можно через добавленную ранее задачу Gradle `buildAuroraLibs`:

```shell
./gradlew buildAuroraLibs
```

QtBindings из `suspend`-функции создаст [QFuture](https://doc.qt.io/archives/qt-5.6/qfuture.html),
который можно использовать с [QFutureWatcher](https://doc.qt.io/archives/qt-5.6/qfuture.html),
что позволит использовать весь основной функционал [Coroutines](https://kotlinlang.org/docs/coroutines-overview.html).

Вызвать функцию из Qt можно так:

```cpp
auto fibiWrapperAsync = new QFutureWatcher<QList<int>>();
auto feature = io::github::kotlin::fibonacci::generateFibiWrapperAsync(5);
fibiWrapperAsync->setFuture(feature);
QObject::connect(fibiWrapperAsync, &QFutureWatcher<QList<int>>::finished, [=]() {
    qDebug() << fibiWrapperAsync->result();
    fibiWrapperAsync->deleteLater();
});
```

![preview](../../assets/images/qt-bindings/hw7.png)

QtBindings позволяет завершать запросы и получать исключения из `suspend`-функций.
Добавим метод, на котором можно это проверить:

```kotlin
@QtExport
suspend fun testCatchAsync(isError: Boolean): Boolean {
    delay(5000L)
    if (isError) {
        throw RuntimeException("My custom error")
    }
    return true
}
```

Вот так можно вызвать функцию `testCatchAsync` с отменой её ожидания:

```cpp
auto testCatchAsyncCancel = new QFutureWatcher<bool>();
auto featureCancel = io::github::kotlin::fibonacci::testCatchAsync(false);
testCatchAsyncCancel->setFuture(featureCancel);
QObject::connect(testCatchAsyncCancel, &QFutureWatcher<bool>::canceled, [=]() {
    qDebug() << "testCatchAsyncCancel - canceled";
});
// Отмена
testCatchAsyncCancel->cancel();
```

![preview](../../assets/images/qt-bindings/hw8.png)

Для перехвата ошибки в функции `testCatchAsync` укажем параметр `isError` как `true` и воспользуемся `try/catch`:

```cpp
auto testCatchAsyncError = new QFutureWatcher<bool>();
auto featureError = io::github::kotlin::fibonacci::testCatchAsync(true);
testCatchAsyncError->setFuture(featureError);
QObject::connect(testCatchAsyncError, &QFutureWatcher<bool>::canceled, [=]() {
    try {
        auto value =  testCatchAsyncError->result();
        qDebug() << value;
    } catch (Aurora::Kmp::QtBindings::CoroutineException& ex) {
        qDebug() << ex.message();
    }
});
```

![preview](../../assets/images/qt-bindings/hw9.png)
