Asynchronous programming in C++ using resumable functions and await

December 20th, 2013

As you know we recently released the Visual C++ Compiler November 2013 CTP. One of the many features in this CTP is the support for resumable functions and await. In this blog post, I want to touch upon some examples where these features make the experience of programming with asynchronous API much simpler.

Example 1

The first example we are going to look at is the official File picker sample for Windows 8.1. If you open the solution for this sample using Visual Studio 2013, build and run, you will see an app like the below. Selecting Option 4 in this sample brings up the file picker dialog which allows you to save a simple text file.

In the project file Scenario4.xaml.cpp, the member function “Scenario4::SaveFileButton_Click” contains the implementation of bringing up file picker and writing to the saved file location. I have removed some code comments for brevity.

Code without await:

void Scenario4::SaveFileButton_Click(Object^ sender, RoutedEventArgs^ e) { rootPage->ResetScenarioOutput(OutputTextBlock); FileSavePicker^ savePicker = ref new FileSavePicker(); savePicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary; auto plainTextExtensions = ref new Platform::Collections::Vector<String^>(); plainTextExtensions->Append(".txt"); savePicker->FileTypeChoices->Insert("Plain Text", plainTextExtensions); savePicker->SuggestedFileName = "New Document"; create_task(savePicker->PickSaveFileAsync()).then([this](StorageFile^ file) { if (file != nullptr) { CachedFileManager::DeferUpdates(file); create_task(FileIO::WriteTextAsync(file, file->Name)).then([this, file]() { create_task(CachedFileManager::CompleteUpdatesAsync(file)).then([this, file] (FileUpdateStatus status) { if (status == FileUpdateStatus::Complete) OutputTextBlock->Text = "File " + file->Name + " was saved."; else OutputTextBlock->Text = "File " + file->Name + " couldn't be saved."; }); }); } else { OutputTextBlock->Text = "Operation cancelled."; } }); }

Above code uses PPL Tasksto call Windows Runtime asynchronous API by providing lambdas to handle the results of those API.

Let’s make a few changes to this code now:

I’ll assume that you have already downloaded and installed the November CTP.

In the project properties, change the platform toolset to “ Visual C++ Compiler Nov 2013 CTP (CTP_Nov2013) ”

” Open up the file Scenario4.xaml.h and to the class “Scenario4”, add a private function with the following signature:

void SaveFileButtonWithAwait() __resumable;

Open up the file Scenario4.xaml.cpp and below the existing include statements, add the following:

#include <pplawait.h>

In the same file, go to the existing member function “Scenario4::SaveFileButton_Click” and comment out all of its contents. Instead add a simple call to the newly added member function:

SaveFileButtonWithAwait();

Provide the implementation of the member function we earlier added to the header file. The code looks like:

Code with await:

void Scenario4::SaveFileButtonWithAwait() __resumable { rootPage->ResetScenarioOutput(OutputTextBlock); FileSavePicker^ savePicker = ref new FileSavePicker(); savePicker->SuggestedStartLocation = PickerLocationId::DocumentsLibrary; auto plainTextExtensions = ref new Platform::Collections::Vector<String^>(); plainTextExtensions->Append(".txt"); savePicker->FileTypeChoices->Insert("Plain Text", plainTextExtensions); savePicker->SuggestedFileName = "New Document"; auto file = __await savePicker->PickSaveFileAsync(); if (file != nullptr) { CachedFileManager::DeferUpdates(file); __await FileIO::WriteTextAsync(file, file->Name); auto status = __await CachedFileManager::CompleteUpdatesAsync(file); if (status == FileUpdateStatus::Complete) { OutputTextBlock->Text = "File " + file->Name + " was saved."; } else { OutputTextBlock->Text = "File " + file->Name + " couldn't be saved."; } } else { OutputTextBlock->Text = "Operation cancelled."; } }

Above code uses await to – well – wait for the result of the asynchronous API. If you contrast this code (using await) with the earlier code (using PPL tasks), you will agree that while both get the job done, the latter is definitely better looking.

Example 2

Another example (not present as an online sample but used in a real app) is the below code. It basically calls the Windows Runtime FilePicker API to select multiple pictures and then creates multiple tasks to copy all of the selected files to the application’s temporary folder. Before proceeding it needs to wait till all files are copied.

Code without await:

void XamlSpiro::MainPage::loadImagesWithPPL() { auto openPicker = ref new FileOpenPicker(); openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary; openPicker->ViewMode = PickerViewMode::Thumbnail; openPicker->FileTypeFilter->Append("*"); task<IVectorView<StorageFile^>^>(openPicker->PickMultipleFilesAsync()).then([this] (IVectorView<StorageFile^>^ fileVector) { for (auto file : fileVector) { m_copyTasks.push_back(create_task(file->CopyAsync( ApplicationData::Current->TemporaryFolder, file->Name, NameCollisionOption::ReplaceExisting))); } when_all(begin(m_copyTasks), end(m_copyTasks)).then([this](std::vector<StorageFile^> results) { for (auto copiedFile : results) { InputFilesVector->Append(copiedFile); } }).then([this]() { DisplayImages(); }); }); }

Code with await:

void XamlSpiro::MainPage::loadImagesWithAwait() __resumable { auto openPicker = ref new FileOpenPicker(); openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary; openPicker->ViewMode = PickerViewMode::Thumbnail; openPicker->FileTypeFilter->Append("*"); auto fileVector = __await openPicker->PickMultipleFilesAsync(); for (auto file : fileVector) { m_copyTasks.push_back(create_task(file->CopyAsync(ApplicationData::Current->TemporaryFolder, file->Name, NameCollisionOption::ReplaceExisting))); } auto results = __await when_all(begin(m_copyTasks), end(m_copyTasks)); for (auto copiedFile : results) { InputFilesVector->Append(copiedFile); } DisplayImages(); }

A subtle difference in this case is that we are not unnecessarily calling await for every CopyAsync call. That would have been sub-optimal. Instead we are still creating individual tasks for all copy operations and calling await only on the when_all operation so that we wait only for the required amount of time, no more, no less.

As you might know, Windows Store world is full of asynchronous Windows Runtime API. So these features are especially helpful for Store app development. They provide a synchronous way of thinking about code which needs to compose asynchronous calls. We hope you will try these features out and let us know your feedback.