Using C++ Coroutines to simplify async UWP code

The Universal Windows Platform (UWP) introduced many async APIs; there are now almost 1700 of them. In fact, the team switched every API that could take 50ms or more to complete to async mode.

Coding with the async pattern is not an easy task, especially in C++ where you have to create a ppl task and use a continuation (.then) with some lambdas. In fact, in many cases writing the code itself is not so hard, but the readability is not good.

C++ Coroutines can simplify your async code, and make the code easy to understand, write, and maintain. But rather than give you a 1000-word description, let’s look at an example:

In this code we try to open an image, using PickSingleFileAsync and OpenAsync:

void AsyncDemoForBuild::MainPage::PickImageClick(Platform::Object^ sender,
  Windows::UI::Xaml::RoutedEventArgs^ e)
{
  using namespace Windows::UI::Xaml::Media::Imaging;
  using namespace Windows::Storage::Pickers;
  using namespace concurrency;

  auto picker = ref new FileOpenPicker();
  picker->FileTypeFilter->Append(L".jpg");
  picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

  create_task(picker->PickSingleFileAsync()).then([this]
    (Windows::Storage::StorageFile^ file)
  {
    if (nullptr == file)
      return;

    create_task(file->OpenReadAsync()).then([this]
      (Windows::Storage::Streams::IRandomAccessStreamWithContentType^ stream)
    {
      auto bitmap = ref new BitmapImage();
      bitmap->SetSource(stream);
      theImage->Source = bitmap;
      OutputDebugString(L"1. End of OpenReadAsync lambda.\r\n");
    });

    OutputDebugString(L"2. End of PickSingleFileAysnc lambda.\r\n");
  });

  OutputDebugString(L"3. End of function.\r\n");

}

The code introduces complexity because of the async model, but if it was synchronous, it would look a lot nicer:

//Pseudo Code

Void ShowImage()

{

auto picker = ref new FileOpenPicker();

picker->FileTypeFilter->Append(L”.jpg”);

picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

auto file = picker->PickSingleFile();

auto stream = file->OpenRead();

auto bitmap = ref new BitmapImage();

bitmap->SetSource(stream);

theImage->Source = bitmap;

}

With Coroutines, we can use co_await in C++, but we still need to be in a task, so the code could be written like this:

#include <experimental\resumable>

#include <pplawait.h>

using namespace Platform;

task<void> AsyncDemoForBuild::MainPage::PickAnImage()
{
    auto picker = ref new FileOpenPicker();
    picker->FileTypeFilter->Append(L".jpg");
    picker->SuggestedStartLocation = PickerLocationId::PicturesLibrary;

    auto file = co_await picker->PickSingleFileAsync();
    if (nullptr == file)
        return;

    auto stream = co_await file->OpenReadAsync();

    auto bitmap = ref new BitmapImage();
    bitmap->SetSource(stream);
    theImage->Source = bitmap;
    OutputDebugString(L"1. End of OpenReadAsync lambda.\r\n");
}

And we could call it this way:

void AsyncDemoForBuild::MainPage::PickImageClick(Platform::Object^ sender,  Windows::UI::Xaml::RoutedEventArgs^ e)
{
    PickAnImage();
}

As you can see in this sample, UWP C++ code can be made much simpler by using co_await; almost as simple as the synchronous form. We see also that co_await can be used with C++/Cx code, meaning you can use ‘^’ references without any ambiguities.

Of course, the code has to be compiled using the /await option in the command line:

clip_image002[12]

It’s important to note that in Visual Studio 2015, Update2 you can also use the /SDL option.

This form of Coroutine (co_await) is the easiest way to use Coroutines. However, Coroutines in C++ can do much more. For example, you can:

· Define new awaitables to customize await for your environment using existing coroutine types.

· Define new coroutine types.

Have a look at this post about customized awaiters. We’ll have more posts to come on other async coding subjects.

Note that Coroutines are not yet part of the C++ standard, but are only found in a TS (Technical Specification) and needs to be seen as experimental (more info here). However, since we removed some compatibility friction with the /RTC and /SDL options in VS2015 Update2, we consider Coroutines ready for production. Please let us know about your experiments, your questions, and any issues you find.

We recorded a video about this for //build 2016.