GithubHelp home page GithubHelp logo

danvratil / qcoro Goto Github PK

View Code? Open in Web Editor NEW
320.0 320.0 53.0 1.16 MB

C++ Coroutines for Qt

Home Page: https://qcoro.dvratil.cz

License: MIT License

CMake 17.77% C++ 80.91% C 1.22% Shell 0.10%
async coroutines cpp cpp20 qt qt5 qt6

qcoro's People

Contributors

a17r avatar alex-viking avatar captainurist avatar carlschwan avatar dangelog avatar danvratil avatar dependabot[bot] avatar developerpaul123 avatar dfaure avatar hannahkiekens avatar heirecka avatar ivknv avatar jbruechert avatar micahspikah avatar nicolasfella avatar nmariusp avatar r-value avatar rgriebl avatar shaan7 avatar sizeofvoid avatar vicr123 avatar xvitaly avatar ysc3839 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qcoro's Issues

qcorotcpserver ASAN error with clang 15

Some time during May something has changed in clang that causes qcorotcpservertest to fail under ASAN.

  • Investigate what change to compiler has triggered this
  • Investigate whether it is a real bug in our code, or miscompilation

co_awaiting multiple tasks in parallel

I have already ran into a case in QCoro where being able to co_await multiple tasks in parallel would make the code simpler , and I think there's a lot of potential hidden in this feature.

The idea of for the API is:

// co_await two tasks, return result of both as a tuple
template<typename T1, typename T2>
Task<std::tuple<T1, T2>> operator && (Task<T1>, Task<T2>);
// co_await two tasks, return result of the first task to finish, cancel the other
template<typename T1, typename T2>
Task<std::variant<T1, T2>> operator || (Task<T1>, Task<T2>);

There's a question of composability, .e.g. should

co_await task1 && task2 && task3;

return std::tuple<T1, T2, T3> or std::tuple<std::tuple<T1, T2>, T3>?

Same goes for the operator|| - should the result be std::variant<T1, T2, T3> or std::variant<std::variant<T1, T2>, T3>?

I see benefit in having both, so I guess we could have allOf and firstOf functions to provide the flat result (as shown below) and leave the operator|| and operator&& to return the nested types following the natural rules of operator precedence.

template<typename Ts ...> Task<std::tuple<Ts ...>> allOf(Task<Ts> ...);
template<typename Ts ...> Task<std::variant<Ts ...>> firstOf(Task<Ts> ...);

Stability policy?

We'd like to use QCoro in Plasma, but before we do that we'd like to know what kind of stability (in terms of API/ABI etc) we can expect from new QCoro versions.

We can probably live without ABI stability, but a (reasonably) stable API would be appreciated.

The readme says "This is a rather experimental library that helps me to understand coroutines in C++." Is that still accurate or has the project matured since writing that?

Distinguish between clang and libc++ in macros

Currently we use #ifdef __clang__ to conditionally toggle code, even if the real reason for the toggle is libc++, rather than clang. We should throughly review all the ifdefs and see where the ifdef really should be about libc++ version rather than clang itself.

Fix QCoroNetwork linking for shared library

WaitSignalHelper defined in qcoroiodevice_p.h is used both in core and in network libraries. When using shared linking option, the build fails with log below. Using MSVC 19.29.30140.0 on Windows 10 and Qt 5.15.1.

[15/15] Linking CXX shared library bin\QCoro5Network.dll FAILED: bin/QCoro5Network.dll lib/QCoro5Network.lib cmd.exe /C "cmd.exe /C "E:\.conan\b280e9\1\bin\cmake.exe -E __create_def E:\.conan\ce448f\1\build_subfolder\source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\.\exports.def E:\.conan\ce448f\1\build_subfolder\source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\.\exports.def.objs && cd E:\.conan\ce448f\1\build_subfolder" && E:\.conan\b280e9\1\bin\cmake.exe -E vs_link_dll --intdir=source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir --rc=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\rc.exe --mt=C:\PROGRA~2\WI3CF2~1\10\bin\100183~1.0\x64\mt.exe --manifests -- C:\PROGRA~2\MIB055~1\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\QCoro5Network_autogen\mocs_compilation.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcoroabstractsocket.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcorolocalsocket.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcoronetworkreply.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcorotcpserver.cpp.obj /out:bin\QCoro5Network.dll /implib:lib\QCoro5Network.lib /pdb:bin\QCoro5Network.pdb /dll /version:0.5 /machine:x64 /debug /INCREMENTAL /DEF:source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\.\exports.def lib\QCoro5Core.lib E:\.conan\4d51e9\1\lib\Qt5Network.lib E:\.conan\4d51e9\1\lib\Qt5Core.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib && cd ." LINK Pass 1: command "C:\PROGRA~2\MIB055~1\2019\COMMUN~1\VC\Tools\MSVC\1429~1.301\bin\Hostx64\x64\link.exe /nologo source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\QCoro5Network_autogen\mocs_compilation.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcoroabstractsocket.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcorolocalsocket.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcoronetworkreply.cpp.obj source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\qcorotcpserver.cpp.obj /out:bin\QCoro5Network.dll /implib:lib\QCoro5Network.lib /pdb:bin\QCoro5Network.pdb /dll /version:0.5 /machine:x64 /debug /INCREMENTAL /DEF:source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir\.\exports.def lib\QCoro5Core.lib E:\.conan\4d51e9\1\lib\Qt5Network.lib E:\.conan\4d51e9\1\lib\Qt5Core.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /MANIFESTFILE:source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir/intermediate.manifest source_subfolder\qcoro\network\CMakeFiles\QCoro5Network.dir/manifest.res" failed (exit code 1120) with the following output: Creating library lib\QCoro5Network.lib and object lib\QCoro5Network.exp qcoroabstractsocket.cpp.obj : error LNK2001: unresolved external symbol "public: static struct QMetaObject const QCoro::detail::WaitSignalHelper::staticMetaObject" (?staticMetaObject@WaitSignalHelper@detail@QCoro@@2UQMetaObject@@B) qcorolocalsocket.cpp.obj : error LNK2001: unresolved external symbol "public: static struct QMetaObject const QCoro::detail::WaitSignalHelper::staticMetaObject" (?staticMetaObject@WaitSignalHelper@detail@QCoro@@2UQMetaObject@@B) qcoronetworkreply.cpp.obj : error LNK2001: unresolved external symbol "public: static struct QMetaObject const QCoro::detail::WaitSignalHelper::staticMetaObject" (?staticMetaObject@WaitSignalHelper@detail@QCoro@@2UQMetaObject@@B) bin\QCoro5Network.dll : fatal error LNK1120: 1 unresolved externals ninja: build stopped: subcommand failed.

Investigate ASAN issues on Qt5 Windows build

The CI is failing due to ASAN issues on Qt5/Windows.

I currently have no means of debugging this issue locally, so ASAN is disabled on CI for Qt5 Windows for now, but I'd like to

  1. get better symbol names
  2. find out whether it is a real issue or a false positive
  3. either fix the problem in QCoro or create a supression rule

Details below:

test 12
      Start 12: test-qcoronetworkreply

12: Test command: D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe
12: Test timeout computed to be: 1500
12: warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use
12: QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.
12: ********* Start testing of QCoroNetworkReplyTest *********
12: Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-llp64 shared (dynamic) release build; by MSVC 2019), windows server2016
12: PASS   : QCoroNetworkReplyTest::initTestCase()
12: =================================================================
12: ==5180==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x0229a2492cc0 in thread T2
12:     #0 0x7ff970beedb9  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18004edb9)
12:     #1 0x7ff9a1cef4bd  (C:\Windows\System32\ucrtbase.dll+0x18000f4bd)
12:     #2 0x7ff9a1cef297  (C:\Windows\System32\ucrtbase.dll+0x18000f297)
12:     #3 0x7ff9a1cef13a  (C:\Windows\System32\ucrtbase.dll+0x18000f13a)
12:     #4 0x7ff9a1cef0e7  (C:\Windows\System32\ucrtbase.dll+0x18000f0e7)
12:     #5 0x7ff9719dc614  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1802dc614)
12:     #6 0x7ff9719dc630  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1802dc630)
12:     #7 0x7ff971855886  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180155886)
12:     #8 0x7ff971854266  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180154266)
12:     #9 0x7ff9717205a9  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1800205a9)
12:     #10 0x7ff97172051b  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x18002051b)
12:     #11 0x7ff971819c7b  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180119c7b)
12:     #12 0x7ff73a6e2c0d  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe+0x140002c0d)
12:     #13 0x7ff971725ee0  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180025ee0)
12:     #14 0x7ff970bfcb53  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18005cb53)
12:     #15 0x7ff9a2dc4ecf  (C:\Windows\System32\KERNEL32.DLL+0x180014ecf)
12:     #16 0x7ff9a478e39a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e39a)
12: 
12: Address 0x0229a2492cc0 is a wild pointer.
12: SUMMARY: AddressSanitizer: bad-free (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18004edb9) 
12: Thread T2 created by T0 here:
12:     #0 0x7ff970bfd978  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18005d978)
12:     #1 0x7ff971725c68  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180025c68)
12:     #2 0x7ff73a6e3aee  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe+0x140003aee)
12:     #3 0x7ff73a6e7928  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe+0x140007928)
12:     #4 0x7ff9718d892c  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1801d892c)
12:     #5 0x7ff98c4758de  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800058de)
12:     #6 0x7ff98c47554e  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x18000554e)
12:     #7 0x7ff98c47652d  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x18000652d)
12:     #8 0x7ff98c4799c4  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800099c4)
12:     #9 0x7ff98c4774f3  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800074f3)
12:     #10 0x7ff73a6ec530  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe+0x14000c530)
12:     #11 0x7ff73a6ffd0f  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-qcoronetworkreply.exe+0x14001fd0f)
12:     #12 0x7ff9a2dc4ecf  (C:\Windows\System32\KERNEL32.DLL+0x180014ecf)
12:     #13 0x7ff9a478e39a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e39a)
12: 
12: ==5180==ABORTING
12/14 Test #12: test-qcoronetworkreply ...........***Failed    2.28 sec
warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use
QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.
test 14
      Start 14: test-testhttpserver

14: Test command: D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe
14: Test timeout computed to be: 1500
14: warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use
14: QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.
14: ********* Start testing of TestHttpServerTest *********
14: Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-llp64 shared (dynamic) release build; by MSVC 2019), windows server2016
14: PASS   : TestHttpServerTest::initTestCase()
14: =================================================================
14: ==6908==ERROR: AddressSanitizer: attempting free on address which was not malloc()-ed: 0x0221ec763a30 in thread T2
14:     #0 0x7ff970beedb9  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18004edb9)
14:     #1 0x7ff9a1cef4bd  (C:\Windows\System32\ucrtbase.dll+0x18000f4bd)
14:     #2 0x7ff9a1cef297  (C:\Windows\System32\ucrtbase.dll+0x18000f297)
14:     #3 0x7ff9a1cef13a  (C:\Windows\System32\ucrtbase.dll+0x18000f13a)
14:     #4 0x7ff9a1cef0e7  (C:\Windows\System32\ucrtbase.dll+0x18000f0e7)
14:     #5 0x7ff9719dc614  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1802dc614)
14:     #6 0x7ff9719dc630  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1802dc630)
14:     #7 0x7ff971855886  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180155886)
14:     #8 0x7ff971854266  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180154266)
14:     #9 0x7ff9717205a9  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1800205a9)
14:     #10 0x7ff97172051b  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x18002051b)
14:     #11 0x7ff971819c7b  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180119c7b)
14:     #12 0x7ff759352a3d  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe+0x140002a3d)
14:     #13 0x7ff971725ee0  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180025ee0)
14:     #14 0x7ff970bfcb53  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18005cb53)
14:     #15 0x7ff9a2dc4ecf  (C:\Windows\System32\KERNEL32.DLL+0x180014ecf)
14:     #16 0x7ff9a478e39a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e39a)
14: 
14: Address 0x0221ec763a30 is a wild pointer.
14: SUMMARY: AddressSanitizer: bad-free (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18004edb9) 
14: Thread T2 created by T0 here:
14:     #0 0x7ff970bfd978  (C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\MSVC\14.31.31103\bin\HostX64\x64\clang_rt.asan_dynamic-x86_64.dll+0x18005d978)
14:     #1 0x7ff971725c68  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x180025c68)
14:     #2 0x7ff75935391e  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe+0x14000391e)
14:     #3 0x7ff759355131  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe+0x140005131)
14:     #4 0x7ff9718d892c  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Core.dll+0x1801d892c)
14:     #5 0x7ff98c4758de  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800058de)
14:     #6 0x7ff98c47554e  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x18000554e)
14:     #7 0x7ff98c47652d  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x18000652d)
14:     #8 0x7ff98c4799c4  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800099c4)
14:     #9 0x7ff98c4774f3  (C:\Qt\5.15.2\msvc2019_64\bin\Qt5Test.dll+0x1800074f3)
14:     #10 0x7ff7593560d0  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe+0x1400060d0)
14:     #11 0x7ff759356eaf  (D:\a\qcoro\qcoro\build\tests\RelWithDebInfo\test-testhttpserver.exe+0x140006eaf)
14:     #12 0x7ff9a2dc4ecf  (C:\Windows\System32\KERNEL32.DLL+0x180014ecf)
14:     #13 0x7ff9a478e39a  (C:\Windows\SYSTEM32\ntdll.dll+0x18007e39a)
14: 
14: ==6908==ABORTING
14/14 Test #14: test-testhttpserver ..............***Failed    0.55 sec

QML support

So far QCoro can only be used in C++ code. However, it should be possible to return QCoro::Task<T> from a QObject's invokable method that can be called from QML.

Unfortunately, we are unable to extend the QML engine in such a way to support "async" and "await" keywords to duplicate the ES8 Promise/Async/Await API. However, we should still be able to wrap QCoro::Task<T> into some QML-friendly wrapper that can expose the existing .then() continuations, so that it's possible to asynchronously handle a C++ coroutine result in QML.

Qt5 and Qt6 versions use the same filename and soname

Qt 5 and Qt 6 versions of QCoro use the same filename and soname, so they can't coexist on the same system without using rpath tricks and/or LD_LIBRARY_PATH/LD_PRELOAD and friends.
It may make sense to use a naming scheme similar to Qt's own libraries (--> libQt5Coro.so.* / libQt6Coro.so.*)

CI: Re-enable ASAN in clang-cl builds

I was able to get qcoro compiled with ASAN using clang-cl, but the executables seem broken - they don't start - I suspect it could be the mix of Qt libs and clang's asan (??), but I have no clue, really...

Make exception support compile-time optional

Exceptions are not usually used with Qt and some project may choose to disable exception support (also it's possible to compile Qt without exception support), we should support that do and conditionally disable all exception-related code.

Is passing objects as reference from a coroutine to a coroutine function ok?

An example:

QCoro::Task<> MyClass::doSomethingWithData(QByteArray &data) {

...........................................

QNetworkAccessManager networkAccessManager;
const QNetworkReply *reply = co_await networkAccessManager.get(url);
...............................................

}

QCoro::Task<> MyClass::fetchData() {

QNetworkAccessManager networkAccessManager;
const QNetworkReply *reply = co_await networkAccessManager.get(url);
const auto data = reply->readAll();
doSomethingWithData(data)

}

Rename task.h to qcorotask.h

All headers in QCoro have QCoro prefix, except for Task. There can be different libraries (private or public) which also use this term. It would be more readable and less conflicting to rename it to QCoroTask.

Additionally QCoroTaskFwd could be introduced to reduce code duplication when forward declaring coroutines in headers:

namespace QCoro {
template<typename T>
class Task;
}

qcoroprocess test failed on 0.4.0

+ /usr/bin/ctest --output-on-failure --force-new-ctest-process -j4
Test project /builddir/build/BUILD/qcoro-0.4.0/redhat-linux-build
      Start  1: test-qtimer
      Start  2: test-qcoroprocess
      Start  3: test-qcorosignal
      Start  4: test-task
 1/13 Test  #3: test-qcorosignal .................   Passed    0.31 sec
      Start  5: test-testconstraints
 2/13 Test  #5: test-testconstraints .............   Passed    0.01 sec
      Start  6: test-qfuture
 3/13 Test  #1: test-qtimer ......................   Passed    0.70 sec
      Start  7: test-qdbuspendingcall
 4/13 Test  #6: test-qfuture .....................   Passed    0.91 sec
      Start  8: test-qdbuspendingreply
 5/13 Test  #7: test-qdbuspendingcall ............   Passed    1.02 sec
      Start  9: test-qcorolocalsocket
 6/13 Test  #4: test-task ........................   Passed    1.89 sec
      Start 10: test-qcoroabstractsocket
 7/13 Test  #2: test-qcoroprocess ................***Failed    4.02 sec
********* Start testing of QCoroProcessTest *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 11.2.1 20210728 (Red Hat 11.2.1-1)), fedora 35
PASS   : QCoroProcessTest::initTestCase()
PASS   : QCoroProcessTest::testStartTriggers()
PASS   : QCoroProcessTest::testStartNoArgsTriggers()
FAIL!  : QCoroProcessTest::testStartDoesntBlock() 'eventLoopResponsive' returned FALSE. ()
   Loc: [/builddir/build/BUILD/qcoro-0.4.0/tests/qcoroprocess.cpp(77)]
QWARN  : QCoroProcessTest::testStartDoesntBlock() QProcess: Destroyed while process ("true") is still running.
PASS   : QCoroProcessTest::testStartDoesntCoAwaitRunningProcess()
PASS   : QCoroProcessTest::testFinishTriggers()
PASS   : QCoroProcessTest::testFinishDoesntCoAwaitFinishedProcess()
PASS   : QCoroProcessTest::testFinishCoAwaitTimeout()
PASS   : QCoroProcessTest::cleanupTestCase()
Totals: 8 passed, 1 failed, 0 skipped, 0 blacklisted, 4012ms
********* Finished testing of QCoroProcessTest *********

      Start 11: test-qcoronetworkreply
 8/13 Test  #8: test-qdbuspendingreply ...........   Passed    3.12 sec
      Start 12: test-qcorotcpserver
 9/13 Test #12: test-qcorotcpserver ..............   Passed    0.51 sec
      Start 13: test-testhttpserver
10/13 Test #13: test-testhttpserver ..............   Passed    1.51 sec
11/13 Test  #9: test-qcorolocalsocket ............   Passed    5.05 sec
12/13 Test #10: test-qcoroabstractsocket .........   Passed    5.05 sec
13/13 Test #11: test-qcoronetworkreply ...........   Passed    4.52 sec

92% tests passed, 1 tests failed out of 13

Total Test time (real) =   8.54 sec

The following tests FAILED:
          2 - test-qcoroprocess (Failed)
Errors while running CTest

Initial release?

I'd like to use qcoro in my (KDE) projects. Distros hate shipping stuff from git main branch, so it would be nice to have some form of release. A git tag should do, a downloadable tarball would be even better.

apple-clang support

Hello, I'm currently trying to make a conan package for qcoro on conan-io/conan-center-index#8870,
but the build on apple-clang 12 fails with

/Users/jenkins/w/BuildSingleReference@3/.conan/data/qcoro/0.4.0/_/_/build/77eb698670f46be85ef6b72390c811f44c09c9aa/source_subfolder/qcoro/concepts_p.h:9:10: fatal error: 'concepts' file not found
#include <concepts>

More details on https://c3i.jfrog.io/c3i/misc/logs/pr/8870/5-configs/macos-clang/qcoro/0.4.0//77eb698670f46be85ef6b72390c811f44c09c9aa-build.txt

is apple-clang supported ? If yes, starting with which version ?

Thanks !

A class with `operator co_await()` should be treated as `Awaitable`

According to https://en.cppreference.com/w/cpp/language/coroutines#co_await:
if overload resolution for operator co_await gives a single best overload, the awaiter is the result of that call (awaitable.operator co_await() for member overload, operator co_await(static_cast<Awaitable&&>(awaitable)) for the non-member overload)

qcoro/qcoro/coroutine.h

Lines 23 to 33 in 353aacf

//! A concept describing the Awaitable type
/*!
* Awaitable type is a type that can be passed as an argument to co_await.
*/
template<typename T>
concept Awaitable = requires(T t) {
{ t.await_ready() }
->std::same_as<bool>;
{t.await_suspend(std::declval<QCORO_STD::coroutine_handle<>>())};
{t.await_resume()};
};
Currently co_await a class with operator co_await() cause an error.

clang-cl doesn't recoganise command line option "-fcoroutines-ts"

As titled. But it works like MSVC, leaving blank is OK. Maybe something like below:

macro(qcoro_enable_coroutines)
    if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines")
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
        set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts")
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
        if (MSVC)
            # Clang-cl works like MSVC that auto-enables coroutine support when C++20 is enabled
        else()
            set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcoroutines-ts")
        endif()
    elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
        # MSVC auto-enables coroutine support when C++20 is enabled
    else()
        message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER_ID} is not currently supported.")
    endif()
endmacro()

Broken examples in docs

Can't find Threads if compiled with Clang

When compile qcoro 0.3.0 with LLVM/Clang 13.0.0 I see this configuration error:

DEBUG util.py:623:  -- Looking for C++ include pthread.h
DEBUG util.py:623:  -- Looking for C++ include pthread.h - not found
DEBUG util.py:621:  CMake Error at /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
DEBUG util.py:621:    Could NOT find Threads (missing: Threads_FOUND)
DEBUG util.py:621:  Call Stack (most recent call first):
DEBUG util.py:621:    /usr/share/cmake/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
DEBUG util.py:621:    /usr/share/cmake/Modules/FindThreads.cmake:238 (FIND_PACKAGE_HANDLE_STANDARD_ARGS)
DEBUG util.py:621:    CMakeLists.txt:61 (find_package)
DEBUG util.py:623:  -- Configuring incomplete, errors occurred!

Full build log: https://file-store.openmandriva.org/api/v1/file_stores/432e3f9a77df2b7bc68ea7c1edbb36ce6018db68.log?show=true

Details:
OS: OpenMandriva Cooker
Compiler: LLVM/Clang 13.0.0
LTO: Enabled

I tried linking -pthread but was same.

Only compilation with GCC fixed the problem.

The problem is that in Mandriva we try to make sure that every (if possible) package is built with Clang.
So it would be great if a Clang compilation would be possible.

qcoro fails to build with gcc 11.3.0 and 12.0.1_pre20220424

...
[ 20%] �[32m�[1mLinking CXX executable await-sync-string�[0m
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/examples/basics && /usr/x86_64-pc-linux-gnu/bin/cmake -E cmake_link_script CMakeFiles/await-sync-string.dir/link.txt --verbose=1
/usr/bin/x86_64-pc-linux-gnu-c++ -march=native -O2 -pipe -Wall -Wextra -Werror -pedantic -fcoroutines -Wl,-O1 -Wl,--as-needed "CMakeFiles/await-sync-string.dir/await-sync-string_autogen/mocs_compilation.cpp.o" "CMakeFiles/await-sync-string.dir/await-sync-string.cpp.o" -o await-sync-string 
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
[ 20%] Built target await-sync-string
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:6�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = const QProcess; FuncPtr = void (QProcess::*)(QProcess::QPrivateSignal); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<QProcess::QPrivateSignal>; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:23:44:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(QProcess::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(QProcess::QPrivateSignal)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(QProcess::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(QProcess::QPrivateSignal)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/waitoperationbase_p.h:11�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:6�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = const QProcess; FuncPtr = void (QProcess::*)(int, QProcess::ExitStatus); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<std::tuple<int, QProcess::ExitStatus> >; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:40:41:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(int, QProcess::ExitStatus)>::QCoroSignal(const QCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(int, QProcess::ExitStatus)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(int, QProcess::ExitStatus)>::QCoroSignal(const QCoro::detail::QCoroSignal<const QProcess, void (QProcess::*)(int, QProcess::ExitStatus)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/waitoperationbase_p.h:11�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroprocess.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:7�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = QCoro::detail::WaitSignalHelper; FuncPtr = void (QCoro::detail::WaitSignalHelper::*)(bool); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<bool>; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:128:29:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(bool)>::QCoroSignal(const QCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(bool)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(bool)>::QCoroSignal(const QCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(bool)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/task.h:12�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:7�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = QCoro::detail::WaitSignalHelper; FuncPtr = void (QCoro::detail::WaitSignalHelper::*)(long long int); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<long long int>; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:133:29:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(long long int)>::QCoroSignal(const QCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(long long int)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(long long int)>::QCoroSignal(const QCoro::detail::QCoroSignal<QCoro::detail::WaitSignalHelper, void (QCoro::detail::WaitSignalHelper::*)(long long int)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/task.h:12�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcoroiodevice.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
make[2]: *** [qcoro/core/CMakeFiles/QCoro5Core.dir/build.make:121: qcoro/core/CMakeFiles/QCoro5Core.dir/qcoroprocess.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....
make[2]: *** [qcoro/core/CMakeFiles/QCoro5Core.dir/build.make:93: qcoro/core/CMakeFiles/QCoro5Core.dir/qcoroiodevice.cpp.o] Error 1
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorothread.cpp:6�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = QThread; FuncPtr = void (QThread::*)(QThread::QPrivateSignal); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<QThread::QPrivateSignal>; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorothread.cpp:24:39:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<QThread, void (QThread::*)(QThread::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<QThread, void (QThread::*)(QThread::QPrivateSignal)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<QThread, void (QThread::*)(QThread::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<QThread, void (QThread::*)(QThread::QPrivateSignal)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/usr/x86_64-pc-linux-gnu/include/qt5/QtCore/qsharedpointer_impl.h:71�[m�[K,
                 from �[01m�[K/usr/x86_64-pc-linux-gnu/include/qt5/QtCore/qsharedpointer.h:48�[m�[K,
                 from �[01m�[K/usr/x86_64-pc-linux-gnu/include/qt5/QtCore/qpointer.h:43�[m�[K,
                 from �[01m�[K/usr/x86_64-pc-linux-gnu/include/qt5/QtCore/QPointer:1�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorothread.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorothread.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
In file included from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorotimer.cpp:6�[m�[K:
/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h: In instantiation of '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type> qCoro(T*, FuncPtr&&, std::chrono::milliseconds) [with T = QTimer; FuncPtr = void (QTimer::*)(QTimer::QPrivateSignal); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<QTimer::QPrivateSignal>; std::chrono::milliseconds = std::chrono::duration<long int, std::ratio<1, 1000> >]�[m�[K':
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:149:45:�[m�[K   required from '�[01m�[KQCoro::Task<typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type::value_type> qCoro(T*, FuncPtr&&) [with T = QTimer; FuncPtr = void (QTimer::*)(QTimer::QPrivateSignal); typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type::value_type = QTimer::QPrivateSignal; typename QCoro::detail::QCoroSignal<T, FuncPtr>::result_type = std::optional<QTimer::QPrivateSignal>]�[m�[K'
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorotimer.cpp:41:23:�[m�[K   required from here
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:131:19:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[KQCoro::detail::QCoroSignal<QTimer, void (QTimer::*)(QTimer::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<QTimer, void (QTimer::*)(QTimer::QPrivateSignal)>&)�[m�[K'
  131 |     auto result = �[01;31m�[Kco_await�[m�[K coroSignal;
      |                   �[01;31m�[K^~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;36m�[Knote: �[m�[K'�[01m�[KQCoro::detail::QCoroSignal<QTimer, void (QTimer::*)(QTimer::QPrivateSignal)>::QCoroSignal(const QCoro::detail::QCoroSignal<QTimer, void (QTimer::*)(QTimer::QPrivateSignal)>&)�[m�[K' is implicitly deleted because the default definition would be ill-formed:
   56 | class �[01;36m�[KQCoroSignal�[m�[K {
      |       �[01;36m�[K^~~~~~~~~~~�[m�[K
�[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorosignal.h:56:7:�[m�[K �[01;31m�[Kerror: �[m�[Kuse of deleted function '�[01m�[Kstd::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = QTimer; _Dp = std::default_delete<QTimer>]�[m�[K'
In file included from �[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/memory:76�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/task.h:12�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorotimer.h:7�[m�[K,
                 from �[01m�[K/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qcoro-0.5.0/qcoro/core/qcorotimer.cpp:5�[m�[K:
�[01m�[K/usr/x86_64-pc-linux-gnu/include/c++/11.3.0/bits/unique_ptr.h:468:7:�[m�[K �[01;36m�[Knote: �[m�[Kdeclared here
  468 |       �[01;36m�[Kunique_ptr�[m�[K(const unique_ptr&) = delete;
      |       �[01;36m�[K^~~~~~~~~~�[m�[K
[ 21%] �[32m�[1mLinking CXX executable testdbusserver�[0m
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/tests && /usr/x86_64-pc-linux-gnu/bin/cmake -E cmake_link_script CMakeFiles/testdbusserver.dir/link.txt --verbose=1
/usr/bin/x86_64-pc-linux-gnu-c++ -march=native -O2 -pipe -Wall -Wextra -Werror -pedantic -fcoroutines -Wl,-O1 -Wl,--as-needed CMakeFiles/testdbusserver.dir/testdbusserver_autogen/mocs_compilation.cpp.o CMakeFiles/testdbusserver.dir/testdbusserver.cpp.o -o testdbusserver  /usr/x86_64-pc-linux-gnu/lib/libQt5DBus.so.5.15.3 /usr/x86_64-pc-linux-gnu/lib/libQt5Core.so.5.15.3 
[ 22%] �[32m�[1mLinking CXX executable await-async-string�[0m
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/examples/basics && /usr/x86_64-pc-linux-gnu/bin/cmake -E cmake_link_script CMakeFiles/await-async-string.dir/link.txt --verbose=1
/usr/bin/x86_64-pc-linux-gnu-c++ -march=native -O2 -pipe -Wall -Wextra -Werror -pedantic -fcoroutines -Wl,-O1 -Wl,--as-needed "CMakeFiles/await-async-string.dir/await-async-string_autogen/mocs_compilation.cpp.o" "CMakeFiles/await-async-string.dir/await-async-string.cpp.o" -o await-async-string  /usr/x86_64-pc-linux-gnu/lib/libQt5Core.so.5.15.3 
make[2]: *** [qcoro/core/CMakeFiles/QCoro5Core.dir/build.make:149: qcoro/core/CMakeFiles/QCoro5Core.dir/qcorotimer.cpp.o] Error 1
[ 23%] �[32m�[1mLinking CXX executable dbusserver�[0m
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/examples/dbus/common && /usr/x86_64-pc-linux-gnu/bin/cmake -E cmake_link_script CMakeFiles/dbusserver.dir/link.txt --verbose=1
/usr/bin/x86_64-pc-linux-gnu-c++ -march=native -O2 -pipe -Wall -Wextra -Werror -pedantic -fcoroutines -Wl,-O1 -Wl,--as-needed CMakeFiles/dbusserver.dir/dbusserver_autogen/mocs_compilation.cpp.o CMakeFiles/dbusserver.dir/dbusserver.cpp.o -o dbusserver  /usr/x86_64-pc-linux-gnu/lib/libQt5DBus.so.5.15.3 /usr/x86_64-pc-linux-gnu/lib/libQt5Core.so.5.15.3 
make[2]: *** [qcoro/core/CMakeFiles/QCoro5Core.dir/build.make:135: qcoro/core/CMakeFiles/QCoro5Core.dir/qcorothread.cpp.o] Error 1
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
make[1]: *** [CMakeFiles/Makefile2:1165: qcoro/core/CMakeFiles/QCoro5Core.dir/all] Error 2
make[1]: *** Waiting for unfinished jobs....
[ 24%] �[32m�[1mLinking CXX static library libexamples-dbus-common.a�[0m
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/examples/dbus/common && /usr/x86_64-pc-linux-gnu/bin/cmake -P CMakeFiles/examples-dbus-common.dir/cmake_clean_target.cmake
cd /var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build/examples/dbus/common && /usr/x86_64-pc-linux-gnu/bin/cmake -E cmake_link_script CMakeFiles/examples-dbus-common.dir/link.txt --verbose=1
x86_64-pc-linux-gnu-ar qc libexamples-dbus-common.a "CMakeFiles/examples-dbus-common.dir/examples-dbus-common_autogen/mocs_compilation.cpp.o" "CMakeFiles/examples-dbus-common.dir/dbusserver.cpp.o"
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
x86_64-pc-linux-gnu-ranlib libexamples-dbus-common.a
[ 24%] Built target testdbusserver
[ 24%] Built target await-async-string
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
[ 24%] Built target examples-dbus-common
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
[ 24%] Built target dbusserver
make[2]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
[ 24%] Built target qcoro_test_dbus
make[1]: Leaving directory '/var/tmp/paludis/build/dev-libs-qcoro-0.5.0/work/qt5-build'
make: *** [Makefile:149: all] Error 2

Not sure if that's a problem of gcc or qcoro, but it works with gcc 10.3.0 and clang 13.0.1.

Add support for `Generator<void>`

Generator<void> is useful for creating a simple synchronous coroutine that can be paused and resumed with no other special functionality. As of now Generator<void> in a compile error.

Example usecase: a test driver function for testing a program that uses some sort of an asynchronous messaging mechanism

#include <QCoroGenerator>

struct Connection;
Connection* get_connection();
auto conn = get_connection();

struct Message;
Message currentMessage;

QCoro::Generator<void> test_driver()
{
    co_yield;
    check_message_ok(currentMessage);
    conn->sendMessage(...);
    co_yield;
    check_message_ok(currentMessage);
    conn->sendMessage(...);

    //
    // ...
    //

    co_return;
}

struct App;
App* create_app(Connection*);

int main(int argc, char* argv[])
{
    auto driver = test_driver();
    auto it = driver.begin();

    auto app = create_app(conn);

    connect(conn, &Connection::messageReceived, [&] (const auto& msg) {
        currentMessage = msg;
        ++it;
        if (it == driver.end()) {
            app->exit(0);
        }
    });

    app->exec();
}

Coroutine cancellation

It is desirable that coroutines can be cancelled from the outside. So far I came up with four possible solutions, each with their own set of advantages and drawbacks.

1. Using a stop token

Inside a coroutine, user would obtain a stop token (QCoro::thisCoroutine::stopToken()) for the current coroutine. The coroutine caller could cancel the coroutine through the Task<> object (calling task.cancel()).

➕ Gives the programmer more control over where to terminate the cancelled coroutine, allowing for additional cleanup of resources
➖ The programmer must manually check the stop token after each suspension point - failing so makes the coroutine "uncancellable"
➖ If a coroutine is cancelled because it would never be resumed (e.g. a coroutine awaiting a network message gets cancelled because the network is down, so the message will never arrive and the coroutine will never resume), the cancellation will have no real effect as the coroutine will remain suspended, never reaching the code that checks the stop token. I would consider this a memory leak.
➖ Non-void coroutine must return something, which may make the interface more complex.

QCoro::Task<> myCoro() {
    ...
    const auto result = co_await otherCoro();
    if (QCoro::thisCoroutine::stopToken().stop_requested()) {
        ...
        co_return;
    }
    ...
}

2. Using stop token and artificially resuming the cancelled coroutine

This works the same as the case above, but it tries to solve the last ➖ by artificially resuming the suspended coroutine after it's been cancelled. This allows the coroutine to reach the code that checks the stop token and terminate.

➕ Coroutine termination is always guaranteed (unless the programmer makes a mistake and forgets to handle cancellation)
➖ If the cancelled coroutine is awaiting a result of another awaitable, artifically resuming it requires that we also provide some empty result (so all awaitables would have to have std::optional<T> or std::variant<T, QCoro::Cancelled> return type). This would make the API annoying to use, especially if T would be something like std::expect<U, E>, requiring the caller to first check T or Cancelled_t and if T then check U or E.
➖ Same as with the previous case, if the coroutine is non-void, it must return something.

QCoro::Task<> myCoro() {
    ...
    const auto result = co_await otherCoro();
    if (std::holds_alternative<QCoro::Cancelled>(result)) {
        ...
        co_return;
    }
    ...
}

3. Destroying the coroutine

When user cancels a coroutine, we could simply destroy the coroutine (std::coroutine_handle::destroy()) which takes care of destroying the coroutine's stack, including properly invoking destructors. The major danger of this approach is that it's easy to leak manually aquired resources if the resource lifetime crosses the suspension point.

➕ Always works
➕ Can be implemented completely inside Task<>
➖ Programmer loses control over the cancellation
➖ No chance for manual cleanup before terminating the coroutine can lead to memory leaks

❗Destroying the coroutine state does not automatically cascade to nested coroutines, and it may be tricky/impossible to implement. At best we would need to track currently awaited coroutines inside the cancelled coroutine's promise.

4. Throwing an exception from suspension point

When a suspended coroutine is canceled, it would be resumed, and await_resume() at the coroutine's suspension point would throw an exception (e.g. QCoro::CoroutineCanceled)

➕ Always works, as long as the awaitable that the coroutine is awaiting supports it
➕ If the programmer needs to handle cancellation in a special way, they can catch the exception, perform any cleanup, and then rethrow the exception to continue with cancelation.
➕ Can fallback to 3) if exceptions are disabled
➖ Requires support in custom awaitables
➖ Exceptions are non-standard pattern in the Qt ecosystem
➖ Requires exception support enabled

QCoro::Task<> myCoro() {
    ...
    try {
        const auto r = co_await otherCoro();
    } except (const QCoro::Cancelled &cancel) {
        ...
        throw cancel;
    }
    ...
}

I'm currently most in favor of 3, since it's the easiest to implement and has a minimal impact on user code. We could always scale up from there. I have yet to do some PoC implementation to verify whether it is workable.

Cannot await QDBusPendingReply with multiple parameters

This code fails to build

QDBusPendingReply<QString, int> reply;
co_await reply;

with

/home/nico/kde/src/plasma-nm/libs/handler.cpp:661:14: error: no matching function for call to 'QCoro::detail::TaskPromise<void>::await_transform(QDBusPendingReply<QString, int>&)'
  661 |     co_await reply;
      |              ^~~~~
In file included from /home/nico/kde/src/plasma-nm/libs/handler.h:21,
                 from /home/nico/kde/src/plasma-nm/libs/handler.cpp:7:
/home/nico/kde/usr/include/qcoro/task.h:164:10: note: candidate: 'template<class T, class Awaiter> auto QCoro::detail::TaskPromiseBase::await_transform(T&&)'
  164 |     auto await_transform(T &&value) {
      |          ^~~~~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:164:10: note:   template argument deduction/substitution failed:
/home/nico/kde/usr/include/qcoro/task.h: In substitution of 'template<class T> using awaiter_type_t = typename QCoro::detail::awaiter_type<T>::type [with T = QDBusPendingReply<QString, int>]':
/home/nico/kde/usr/include/qcoro/task.h:163:26:   required from here
/home/nico/kde/usr/include/qcoro/task.h:32:7: error: invalid use of incomplete type 'struct QCoro::detail::awaiter_type<QDBusPendingReply<QString, int>, void>'
   32 | using awaiter_type_t = typename awaiter_type<T>::type;
      |       ^~~~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:29:8: note: declaration of 'struct QCoro::detail::awaiter_type<QDBusPendingReply<QString, int>, void>'
   29 | struct awaiter_type;
      |        ^~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:190:10: note: candidate: 'template<class T> auto QCoro::detail::TaskPromiseBase::await_transform(QCoro::Task<T>&&)'
  190 |     auto await_transform(QCoro::Task<T> &&task) {
      |          ^~~~~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:190:10: note:   template argument deduction/substitution failed:
/home/nico/kde/src/plasma-nm/libs/handler.cpp:661:14: note:   'QDBusPendingReply<QString, int>' is not derived from 'QCoro::Task<T>'
  661 |     co_await reply;
      |              ^~~~~
In file included from /home/nico/kde/src/plasma-nm/libs/handler.h:21,
                 from /home/nico/kde/src/plasma-nm/libs/handler.cpp:7:
/home/nico/kde/usr/include/qcoro/task.h:196:11: note: candidate: 'template<class T> auto& QCoro::detail::TaskPromiseBase::await_transform(QCoro::Task<T>&)'
  196 |     auto &await_transform(QCoro::Task<T> &task) {
      |           ^~~~~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:196:11: note:   template argument deduction/substitution failed:
/home/nico/kde/src/plasma-nm/libs/handler.cpp:661:14: note:   'QDBusPendingReply<QString, int>' is not derived from 'QCoro::Task<T>'
  661 |     co_await reply;
      |              ^~~~~
In file included from /home/nico/kde/src/plasma-nm/libs/handler.h:21,
                 from /home/nico/kde/src/plasma-nm/libs/handler.cpp:7:
/home/nico/kde/usr/include/qcoro/task.h:202:10: note: candidate: 'template<class T>  requires  Awaitable<T> auto QCoro::detail::TaskPromiseBase::await_transform(T&&)'
  202 |     auto await_transform(T &&awaitable) {
      |          ^~~~~~~~~~~~~~~
/home/nico/kde/usr/include/qcoro/task.h:202:10: note:   template argument deduction/substitution failed:
/home/nico/kde/usr/include/qcoro/task.h:202:10: note: constraints not satisfied
In file included from /home/nico/kde/usr/include/qcoro/task.h:7,
                 from /home/nico/kde/src/plasma-nm/libs/handler.h:21,
                 from /home/nico/kde/src/plasma-nm/libs/handler.cpp:7:
/home/nico/kde/src/plasma-nm/libs/handler.cpp: In substitution of 'template<class T>  requires  Awaitable<T> auto QCoro::detail::TaskPromiseBase::await_transform(T&&) [with T = QDBusPendingReply<QString, int>&]':
/home/nico/kde/src/plasma-nm/libs/handler.cpp:661:14:   required from here
/home/nico/kde/usr/include/qcoro/coroutine.h:46:9:   required for the satisfaction of 'Awaitable<T>' [with T = QDBusPendingReply<QString, int, void, void, void, void, void, void>&]
/home/nico/kde/usr/include/qcoro/coroutine.h:46:53: note: no operand of the disjunction is satisfied
   46 | concept Awaitable = detail::has_operator_coawait<T> ||
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
   47 |                     detail::has_await_methods<T>;
      |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~     

Update to aqtinstall 2.x

There's a new major version of aqtinstall, which is incompatible with the old one, so some changes to the GH Actions script to install Qt are needed.

Currently we are frozen on 1.2.x, but there's a change that it might break if Qt changes something on their side and we will need latest version of aqtinstall anyway - better update it now ;-)

Missing SOVERSION field

Installed shared libraries has no SOVERSION field:

-- Installing: /BUILDROOT/qcoro-0.2.0-1.fc34.x86_64/usr/lib64/libQCoroCore.so

Most of GNU/Linux distributions strictly requires it.

0.6.0 build fails because of -Werror

/build/qcoro/src/qcoro-0.6.0/qcoro/core/qcorosignal.h: In function ‘operator()’:
/build/qcoro/src/qcoro-0.6.0/qcoro/core/qcorosignal.h:225:14: error: ‘MEM[(struct QArrayDataPointer *)&result + 8B].d’ may be used uninitialized [-Werror=maybe-uninitialized]
  225 |         auto result = std::move(mQueue.front());
      |              ^
/build/qcoro/src/qcoro-0.6.0/qcoro/core/qcorosignal.h:225:14: error: ‘MEM[(struct QArrayDataPointer *)&result + 8B].ptr’ may be used uninitialized [-Werror=maybe-uninitialized]

Please don't use -Werror in released code. Even if it works for you, it may break when using a different compiler or a different version of the same compiler, and it will surely break with future compiler versions, so you're artificially setting an expiration date on your code.

A slot called by qt, which is a coroutine: handling exceptions

Hi, I have this kind of code:

#include <iostream>
#include <QTimer>
#include <QCoreApplication>
#include <QCoroSignal>

struct Response {

};

class ServerConnection : public QObject {
    Q_OBJECT
public:
    ServerConnection()
    {
        // connect to the server...
        emit serverConnected();
    }
    Q_SIGNAL void serverConnected();

};

QCoro::Task<Response> sendMessage();

QCoro::Task<> onServerConnected()
{
    std::cerr << "doStuff()\n";
    // ... do stuff, maybe use sendMessage with co_await
    //
    // ...
    //
    throw std::runtime_error("Can't catch this");
}

int main(int argc, char* argv[])
{
    QCoreApplication app(argc, argv);
    auto srv_conn = new ServerConnection();
    QObject::connect(srv_conn, &ServerConnection::serverConnected, onServerConnected);
    app.exec();
}

I want to rework my current app to use qcoro. However, I don't want to rewrite all of the stuff at once. In the above code, I'd like to use coroutines inside the doStuff function. This means that doStuff itself must be a couroutine. My issue is that if doStuff throws, I have no way to handle that exception, because Qt doesn't call it with co_await (I'm not even sure how that would work!).

What do you think is the correct approach? Should I just wrap my "top-level" couroutine with a try-catch block and handle the exceptions there?

Default compiler flags cause build issues on ARM

CMake isn't smart enough to actually print the error even when tracing, but here's what happens with 0.4:

qcore sets '-Wall -Wextra -Werror -pedantic' before looking for Qt.

QtWidgets → QtGui → GLES → EGL and that's where a warning is likely thrown when CMake tries to compile the 'check_cxx_source_compiles' block in FindEGL.cmake.

Either CMAKE_CXX_FLAGS shall be less strict or it shall be moved after finding qcoro's build dependencies.

Investigate websocket test timeouts on Windows CI

I've been seeing those a lot recently, the websockets test fail due to the coroutine timing out. This could point to an actual bug in the WebSockets implementation (at least on Windows) where we are missing some signal. Or it's simply a timeout issue like many we've seen before with the CI... :)

it possible to co_await forever on asyncCall?

I'm writing little usb-stick formatting utility using udisks2 via dbus. QCoro makes code insane simple, instead of chaining singals/slots with QDBusPendingCallWatcher using co_await on asyncCall. But on long operations QtDBus sends timeouts errors.

QCoro::Task<QDBusPendingReply<void>> UDisksHelper::createPartitionTable(const QString &devBlock)
{
    qDebug() << Q_FUNC_INFO;
    QDBusInterface block_iface(UDISKS2_SERVICE, devBlock, UDISKS2_BLOCK_INTERFACE, QDBusConnection::systemBus());
    QVariantMap options;
    options.insert("erase", "zero");
    const QDBusPendingReply<void> empty = co_await block_iface.asyncCall("Format", "empty", options);
    const QDBusPendingReply<void> reply = co_await block_iface.asyncCall("Format", "gpt", QVariantMap());
    co_return reply;
}
Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.", signature="s", contents=("Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.

`QSerialSocket` support

Discussed in #102

Originally posted by DeveloperPaul123 July 28, 2022
I noticed that some subclasses of QIODevice are supported such as QLocalSocket. Are there any plans to support QSerialSocket directly?

Config-time Qt feature detection

Based on #44 it's clear we need to fix feature detection at config time.

  • Enable/disable QtDBus and QtNetwork support not purely based on platform IFs but rather by checking what features Qt is compiled with (because it's possible to have Qt on Linux without QtDBus, and QCoro should automatically detect it and not attempt to build QCoroDBus).
  • Expose information about which QCoro features are enabled in config.h
  • Check for QT_CONFIG(dbus) to adjust default QCORO_WITH_QTDBUS value
  • Check for QT_CONFIG(network) to adjust default QCORO_WITH_QTNETWORK value
  • Check for QT_CONFIG(process) to detect whether Qt is built with QProcess support (#44)
  • Check for QT_CONFIG(future) to detect whether Qt is built with QFuture support
  • Check for QT_CONFIG(localserver) to detect whether Qt is built with QLocalServer support (for QLocalSocket)
  • Check for QT_NO_EXCEPTION to detect whether Qt is built with exception support (#28)

QMake configuration

There's demand for QMake support. I can either borrow the respective scripts from extra-cmake-modules or craft them manually, depending on how complex the config files are.

CI: GCC 12 support

Extend the matrix with GCC 12.

I couldn't find any PPA with gcc-12 for ubuntu 20.04 (used on the linux github runners), so we'll probably have to have a Docker image based on newer ubuntu with gcc 12 and necessary dev tools to build QCoro preinstalled.

Awaiting with context object.

In some cases, one might want to update an object (for example a model), when a coroutine finished.
This can be done by either using a lambda that captures this as a coroutine, or by adding a coroutine member function.
However there is no guarantee that once the coroutine finishes, the object still exists, so this can lead to crashes.

It is pretty much the same issue as the 3-arg connect in Qt: https://github.com/KDE/clazy/blob/master/docs/checks/README-connect-3arg-lambda.md

Is this already a solved problem in some way that I have missed?

If not, I propose adding an overload of QCoro::Task<T>::then that takes a QObject pointer as an additional argument, so that it can connect to QObject::destroyed of the context object, and not invoke the continuation if the signal has been emitted in the meantime.

I currently can't find a good way to fix this when using the co_await keyword, but as this mostly concerns the issue of interaction between coroutine code and sync code, support in .then is probably enough.

If needed, there could be something like

co_await someFunctionReturningTask().withContext(this)

to inject the context even though using co_await.

I already have code for some of the proposed things here, so I can work on implementing it if needed.

Documentation issue

On the documentation site, the "Continue Reading" links of QCoro 0.6.0 Release Announcement and others, jump to nothing.
(https://qcoro.dvratil.cz/news/#blog-p1)

Also, the way the QCoro 0.6 introduction is written is misleading in the sense one could think "apple-clang support" is deprecated, while I think you mean the opposite.

Some tests failed on 0.4.0 due to low timeout

Sometimes qcoroabstractsocket test fails (50/50) on 0.4.0:

+ /usr/bin/ctest --output-on-failure --force-new-ctest-process -j4 --exclude-regex test-qcoroprocess
Test project /builddir/build/BUILD/qcoro-0.4.0/release-qt5/redhat-linux-build
      Start  1: test-qtimer
      Start  2: test-qcorosignal
      Start  3: test-task
      Start  4: test-testconstraints
 1/12 Test  #4: test-testconstraints .............   Passed    0.01 sec
      Start  5: test-qfuture
 2/12 Test  #2: test-qcorosignal .................   Passed    0.31 sec
      Start  6: test-qdbuspendingcall
 3/12 Test  #1: test-qtimer ......................   Passed    0.72 sec
      Start  7: test-qdbuspendingreply
 4/12 Test  #5: test-qfuture .....................   Passed    0.91 sec
      Start  8: test-qcorolocalsocket
 5/12 Test  #6: test-qdbuspendingcall ............   Passed    1.12 sec
      Start  9: test-qcoroabstractsocket
 6/12 Test  #3: test-task ........................   Passed    1.88 sec
      Start 10: test-qcoronetworkreply
 7/12 Test  #7: test-qdbuspendingreply ...........   Passed    3.12 sec
      Start 11: test-qcorotcpserver
 8/12 Test #11: test-qcorotcpserver ..............   Passed    0.51 sec
      Start 12: test-testhttpserver
 9/12 Test #12: test-testhttpserver ..............   Passed    1.52 sec
10/12 Test  #8: test-qcorolocalsocket ............   Passed    5.05 sec
11/12 Test #10: test-qcoronetworkreply ...........   Passed    4.52 sec
12/12 Test  #9: test-qcoroabstractsocket .........***Failed    5.05 sec
********* Start testing of QCoroAbstractSocketTest *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 11.2.1 20210728 (Red Hat 11.2.1-1)), fedora 35
PASS   : QCoroAbstractSocketTest::initTestCase()
PASS   : QCoroAbstractSocketTest::testWaitForConnectedTriggers()
PASS   : QCoroAbstractSocketTest::testWaitForConnectedTimeout()
PASS   : QCoroAbstractSocketTest::testWaitForDisconnectedTriggers()
PASS   : QCoroAbstractSocketTest::testWaitForDisconnectedTimeout()
FAIL!  : QCoroAbstractSocketTest::testDoesntCoAwaitConnectedSocket() No incoming connection within timeout!
   Loc: [/builddir/build/BUILD/qcoro-0.4.0/tests/testhttpserver.h(99)]
PASS   : QCoroAbstractSocketTest::testDoesntCoAwaitDisconnectedSocket()
PASS   : QCoroAbstractSocketTest::testConnectToServerWithArgs()
PASS   : QCoroAbstractSocketTest::testReadAllTriggers()
PASS   : QCoroAbstractSocketTest::testReadTriggers()
PASS   : QCoroAbstractSocketTest::testReadLineTriggers()
PASS   : QCoroAbstractSocketTest::cleanupTestCase()
Totals: 11 passed, 1 failed, 0 skipped, 0 blacklisted, 5041ms
********* Finished testing of QCoroAbstractSocketTest *********


92% tests passed, 1 tests failed out of 12

Total Test time (real) =   6.48 sec

The following tests FAILED:
          9 - test-qcoroabstractsocket (Failed)
Errors while running CTest

Early co_returns do not resume the caller

Maybe I'm not understanding correctly how coroutines work, but I think the following example should work for both cases: returning early and awaited from testReturn.
What I'm seeing (gcc 11 and MSVC 2019) is that testImmediate just hangs and never returns from its co_await

static QCoro::Task<bool> testReturn(bool immediate)
{
    qWarning() << "Running testReturn(immediate:" << immediate << ")";
    if (immediate) {
        co_return true;
    } else {
        QTimer t;
        t.start(100);
        co_await(t);
        co_return true;
    }
}

static QCoro::Task<> testImmediate()
{
    qWarning() << "Starting Immediate...";
    bool b = co_await testReturn(true);
    qWarning() << "Immediate result:" << b;
}

static QCoro::Task<> testDelayed()
{
    qWarning() << "Starting Delayed...";
    bool b = co_await testReturn(false);
    qWarning() << "Delayed result:" << b;
}

int main(int argc, char **argv)
{
    QCoreApplication a(argc, argv);
    QMetaObject::invokeMethod(qApp, []{ testImmediate(); }, Qt::QueuedConnection);
    QMetaObject::invokeMethod(qApp, []{ testDelayed(); }, Qt::QueuedConnection);
    return a.exec();
}

Output:

Starting Immediate...
Running testReturn(immediate: true )
Starting Delayed...
Running testReturn(immediate: false )
Delayed result: true

Doesn't work on Debian 11?

Debian 11: gcc version 10.2.1
Build is fine after lowering cmake version requirement to 3.18 and removing include(HasCompatibleStlAbi) (doesn't work under 3.18 i think).

But in QtCreator in getting compile error on code:

const QDBusReply<UDisks2::Introspection> reply = co_await udisks2_iface.asyncCall(QStringLiteral("GetManagedObjects"));

error: unable to find the promise type for this coroutine

QtCreator doesn't like co_await keyword https://i.imgur.com/fUR5LRQ.png set(CMAKE_CXX_STANDARD 20) enabled

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.