Exemple de source de saisie semi-automatique

Notes

En guise d’alternative à l’exemple de code de cette rubrique, il existe le code source d’une version prête pour la production d’une implémentation de source d’achèvement de tâche dans le dépôt GitHub cpp-async.

Cette rubrique vous montre comment créer et consommer votre propre classe source de complétion, semblable à la classe .NET TaskCompletionSource.

Code source de l’exemple completion_source

Le code figurant dans le listing ci-dessous est donné à titre d’exemple. Son but est de vous montrer comment vous pouvez écrire votre propre version. Par exemple, la prise en charge de l’annulation et la propagation des erreurs n’entrent pas dans le cadre de cet exemple.

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    completion_source()
    {
        m_signal.attach(::CreateEvent(nullptr, true, false, nullptr));
    }

    void set(T const& value)
    {
        m_value = value;
        ::SetEvent(m_signal.get());
    }

    bool await_ready() const noexcept
    {
        return ::WaitForSingleObject(m_signal.get(), 0) == 0;
    }

    void await_suspend(std::experimental::coroutine_handle<> resume)
    {
        m_wait.attach(winrt::check_pointer(::CreateThreadpoolWait(callback, resume.address(), nullptr)));
        ::SetThreadpoolWait(m_wait.get(), m_signal.get(), nullptr);
    }

    T await_resume() const noexcept
    {
        return m_value;
    }

private:

    static void __stdcall callback(PTP_CALLBACK_INSTANCE, void* context, PTP_WAIT, TP_WAIT_RESULT) noexcept
    {
        std::experimental::coroutine_handle<>::from_address(context)();
    }

    struct wait_traits
    {
        using type = PTP_WAIT;

        static void close(type value) noexcept
        {
            ::CloseThreadpoolWait(value);
        }

        static constexpr type invalid() noexcept
        {
            return nullptr;
        }
    };

    winrt::handle m_signal;
    winrt::handle_type<wait_traits> m_wait;
    T m_value{};
};

Décharger la complétion vers une coroutine distincte

Cette section présente un cas d’usage pour completion_source. Créez un projet dans Visual Studio basé sur le modèle de projet Windows Console Application (C++/WinRT) , puis collez le listing de code suivant dans main.cpp (en développant la définition de completion_source basée sur le listing de la section précédente).

// main.cpp
#include "pch.h"

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    ... // Paste the listing of completion_source here.
}

using namespace std::literals;
using namespace winrt;
using namespace Windows::Foundation;

fire_and_forget CompleteAfterFiveSecondsAsync(completion_source<bool>& completionSource)
{
    co_await 5s;
    completionSource.set(true);
}

IAsyncAction CompletionSourceExample1Async()
{
    completion_source<bool> completionSource;
    CompleteAfterFiveSecondsAsync(completionSource);
    co_await completionSource;
}

int main()
{
    auto asyncAction { CompletionSourceExample1Async() };
    puts("waiting");
    asyncAction.get();
    puts("done");
}

Encapsuler un completion_source dans une classe et retourner une valeur

Dans l’exemple suivant, une classe App simple est utilisée pour encapsuler un completion_source et retourner une valeur une fois l’opération terminée. Créez un projet dans Visual Studio basé sur le modèle de projet Windows Console Application (C++/WinRT) , puis collez le listing de code suivant dans main.cpp (en développant la définition de completion_source basée sur le listing de la section précédente).

// main.cpp
#include "pch.h"

#include <winrt/base.h>
#include <windows.h>

template <typename T>
struct completion_source
{
    ... // Paste the listing of completion_source here.
}

using namespace std::literals;
using namespace winrt;
using namespace Windows::Foundation;

struct App
{
    completion_source<winrt::hstring> m_completionSource;

    IAsyncOperation<winrt::hstring> CompletionSourceExample2Async()
    {
        co_return co_await m_completionSource;
    }

    winrt::fire_and_forget CompleteAfterFiveSecondsAsync()
    {
        co_await 5s;
        m_completionSource.set(L"Hello, World!");
    }
};

int main()
{
    App app;
    auto asyncAction{ app.CompletionSourceExample2Async() };
    app.CompleteAfterFiveSecondsAsync();
    puts("waiting");
    auto message = asyncAction.get();
    printf("%ls\n", message.c_str());
}