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
UWP_Await::MainPage::button_Click(Platform::Object^
sender,
Windows::UI::Xaml::RoutedEventArgs^
e)
{
auto
openPicker = ref
new
Windows::Storage::Pickers::FileOpenPicker();
openPicker->SuggestedStartLocation =
Windows::Storage::Pickers::PickerLocationId::PicturesLibrary;
openPicker->FileTypeFilter->Append(“.jpg”);
openPicker->FileTypeFilter->Append(“.png”);
create_task(openPicker->PickSingleFileAsync()).then([=](StorageFile^
file)
{
if
(file
!= nullptr) {
create_task(file->OpenAsync(Windows::Storage::FileAccessMode::Read)).then([=](IRandomAccessStream^
stream)
{
auto
bitmapImage = ref
new
BitmapImage();
bitmapImage->SetSource(stream);
Img->Source =
bitmapImage;
});
}
});
}
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:
task<void>
PickaFile2(Windows::UI::Xaml::Controls::Image^
Img)
{
auto
openPicker = ref
new
Windows::Storage::Pickers::FileOpenPicker();
openPicker->SuggestedStartLocation =
Windows::Storage::Pickers::PickerLocationId::PicturesLibrary;
openPicker->FileTypeFilter->Append(“.jpg”);
openPicker->FileTypeFilter->Append(“.png”);
auto
file = co_await openPicker->PickSingleFileAsync();
if
(file != nullptr)
{
auto
stream = co_await
file->OpenAsync(Windows::Storage::FileAccessMode::Read);
auto
bitmapImage = ref
new
BitmapImage();
bitmapImage->SetSource(stream);
Img->Source = bitmapImage;
}
}
And we could call it this way:
void
UWP_Await::MainPage::button2_Click(Platform::Object^
sender,
Windows::UI::Xaml::RoutedEventArgs^
e)
{
PickaFile2(Img);
}
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:
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.