File: await-sync-string.cpp

package info (click to toggle)
qcoro 0.12.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,700 kB
  • sloc: cpp: 8,573; python: 32; xml: 26; makefile: 23; sh: 15
file content (155 lines) | stat: -rw-r--r-- 5,785 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-FileCopyrightText: 2021 Daniel Vrátil <dvratil@kde.org>
//
// SPDX-License-Identifier: MIT

#include "qcoro/coroutine.h"

#include <iostream>
#include <string>

// Awaiter is a concept that provides the await_* methods below, which are used by the
// co_await expression.
// Type is Awaitable if it supports the `co_await` operator.
//
// When compiler sees a `co_await <expr>`, it first tries to obtain an Awaitable type for
// the expression result result type:
//  - first by checking if the current coroutine's promise type has `await_transform()`
//    that for given type returns an Awaitable
//  - if it does not have await_transform, it treats the result type as awaitable.
// Thus, if the current promise type doesn't have compatible `await_transform()` and the
// type itself is not Awaitable, it cannot be `co_await`ed.
//
// If the Awaitable object has `operator co_await` overload, it calls it to obtain the
// Awaiter object. Otherwise the Awaitable object is used as an Awaiter.
//
class StringAwaiter {
public:
    explicit StringAwaiter(const std::string &value) noexcept : mValue(value) {
        std::cout << "StringAwaiter constructed with value '" << value << "'." << std::endl;
    }
    ~StringAwaiter() {
        std::cout << "StringAwaiter destroyed." << std::endl;
    }

    bool await_ready() noexcept {
        std::cout << "StringAwaiter::await_ready() called." << std::endl;
        return false;
    }
    void await_suspend(std::coroutine_handle<>) noexcept {
        std::cout << "StringAwaiter::await_suspend() called." << std::endl;
    }
    std::string await_resume() noexcept {
        std::cout << "StringAwaiter::await_resume() called." << std::endl;
        return mValue;
    }

private:
    std::string mValue;
};

class StringAwaitable {
public:
    StringAwaitable(std::string str) noexcept : mStr(std::move(str)) {
        std::cout << "StringAwaitable constructored with value '" << mStr << "'." << std::endl;
    }

    ~StringAwaitable() {
        std::cout << "StringAwaitable destroyed." << std::endl;
    }

    StringAwaiter operator co_await() {
        std::cout << "StringAwaitable::operator co_await() called." << std::endl;
        return StringAwaiter{mStr};
    }

private:
    std::string mStr;
};

class VoidPromise {
public:
    explicit VoidPromise() {
        std::cout << "VoidPromise constructed." << std::endl;
    }

    ~VoidPromise() {
        std::cout << "VoidPromise destroyed." << std::endl;
    }

    struct promise_type {
        explicit promise_type() {
            std::cout << "VoidPromise::promise_type constructed." << std::endl;
        }

        ~promise_type() {
            std::cout << "VoidPromise::promise_type destroyed." << std::endl;
        }

        // Says whether the coroutine body should be executed immediately (`suspend_never`)
        // or whether it should be executed only once the coroutine is co_awaited.
        std::suspend_never initial_suspend() const noexcept {
            return {};
        }
        // Says whether the coroutine should be suspended after returning a result
        // (`suspend_always`) or whether it should just end and the frame pointer and everything
        // should be destroyed.
        std::suspend_never final_suspend() const noexcept {
            return {};
        }

        // Called by the compiler during initial coroutine setup to obtain the object that
        // will be returned from the coroutine when it is suspended.
        // Sicne this is a promise type for VoidPromise, we return a VoidPromise.
        VoidPromise get_return_object() {
            std::cout << "VoidPromise::get_return_object() called." << std::endl;
            return VoidPromise();
        }

        // Called by the compiler when an exception propagates from the coroutine.
        // Alternatively, we could declare `set_exception()` which the compiler would
        // call instead to let us handle the exception (e.g. propagate it)
        void unhandled_exception() {
            std::terminate();
        }

        // The result of the promise. Since our promise is void, we must implement `return_void()`.
        // If our promise would be returning a value, we would have to implement `return_value()`
        // instead.
        void return_void() const noexcept {};

        StringAwaitable await_transform(std::string str) {
            std::cout << "VoidPromise::await_transform for string '" << str << "' called."
                      << std::endl;
            return StringAwaitable{std::move(str)};
        }
    };
};

std::string regularFunction() {
    return "Hello World!";
}

// This function co_awaits, therefore it's a co-routine and must
// have a promise type to return to the caller.
VoidPromise myCoroutine() {
    // 1. Compiler creates a new coroutine frame `f`
    // 2. Compiler obtains a return object from the promise.
    //    The promise is of type `std::coroutine_traits<CurrentFunctionReturnType>::promise_type`,
    //    which is `CurrentFunctionReturnType::promise_type` (if there is no specialization for
    //    `std::coroutine_traits<CurrentFunctionReturnType>`)
    // 3. Compiler starts execution of the coroutine body by calling `resume()` on the
    //    current coroutine's std::coroutine_handle (obtained from the promise by
    //    `std::coroutine_handle<decltype(f->promise)>::from_promise(f->promise)

    std::cout << "myCoroutine() started." << std::endl;
    const auto result = co_await regularFunction();
    std::cout << "Result successfully co_await-ed: " << result << std::endl;
}

int main() {
    std::cout << "Calling myCoroutine() from main()." << std::endl;
    myCoroutine();
    std::cout << "Returned from myCoroutine() to main()." << std::endl;

    return 0;
}