The C# way
Visual Studio 2012 introduced a simplified approach to asynchronous programming with async and await keywords. By moving all the difficult work to the compiler your code becomes more readable and easier to maintain by resembling synchronous code flow. So loading image from assets and displaying it might look like this.
Nice and simple but that’s C#, what about C++?
The C++ way
Well, that’s a little different. It certainly doesn’t look like synchronous code but it isn’t that bad, right? You could also nest new tasks lambdas instead of using
task_continuation_context like so.
However I prefer
task_continuation_context. Nesting lambdas can easily lead to code beginning in the middle of the screen and you have to remember to copy/reference variables throughout whole code tree.
But wait, isn’t there still something missing here? Hmmm… Oh yes, exceptions! Silly me, how could I forget? But that’s easily fixed, right? All you need to do is put everything in one try catch block, right? No.
Well, that turned ugly really quickly, but what happened? The problem is control flow. In C# a method suspends its progress and yields control to caller at an await and then resumes when it’s ready, so it’s still in try and catch block. In C++ there is no pause and resume. A method performs
create_task() function and moves on, leaving any try catch blocks. Then after async method is done, your lambda body is called on a thread that created the task. Since async method is called on some other thread you can’t catch any exceptions by asking for expected value. You have to ask for task object and perform
get() method to propagate any exception that async method might’ve thrown. Notice that by asking for expected value the
get() method is called implicitly but outside your try catch block.
There is one more important thing. If you’re using
task_continuation_context you must remember to return proper task object for each exit point. If eg. you’ll forget to return task in catch block and you have default compiler settings you’ll only get compiler warning and a runtime crash in case an exception occurs. Also, since you’re required to return task object you’re basically forced to handle normal errors. If I didn’t check
nullptr I would’ve caused another exception and trying to open
MessageDialog when one is already open results in a crash. With lambda nesting you don’t have to worry about it but you’re going to reach that middle of the screen really soon. This example only calls two async methods in succession. Imagine something more complicated, like downloading an image and doing some post-processing on it. Jolly.
You can get around try catch problem by eg. performing everything in your own thread. Thanks to this you can
wait() without fear (calling this on main thread results in uncatchable crash) and get everything in one nice try catch block. Just don’t forget to call UI stuff on main thread.
Developing for Microsoft platforms in C++ you may feel unwelcome. Everyone is writing in C#, examples are scarce and code gets ugly. This is especially problematic in Windows Runtime API (Windows Phone and metro style applications) where virtually every other method is async one. But remember, you have whole C++ standard at your disposal. Try temaplating async tasks into prettier syntax. Check if you can’t get the same effect without using Microsoft API. Get creative, that’s why C++ is such a nice language. It gives you more freedom than other ones. Sure, it’s easier to break things but you won’t get up without falling.