Implementing the try/catch/finally pattern in C++ using lambdas, or why cant this be as easy as it is in C#?

My very first programming 'language' was Z-80 assembler, even before I had learned to program in interpretive BASIC. So you would think I would be used to doing things the hard way, used to pain... =P



When I first saw the C language, I fell in love right away. I already had several CPU dialects under my belt, and the idea of pointers was already an easy notion to grasp -- I had already been using VARPTR in BASIC. The idea of a language that was graceful, provided native pointers, language constructs, NO LINE NUMBERS, who could ask for more?



Then came C++. Well, not exactly. My first exposure to C++ was via the Glockenspiel "compiler", which was really a glorified C preprocessor. No matter, eventually Borland and Microsoft released C++ compilers, and I never looked back.



Until around 2000, when Microsoft released .NET and C#. By this time, we had COM/DCOM, the STL, and boost. Writing custom allocators, IUnknown for custom COM objects, RAII patterns for handling new/delete, you would think that would be enough. But C++ lacked rich type information, was incompatible with C99, and calling conventions, dont even get me started...


C# promised to handle many of these issues, although providing no end to new problems (think IDisposable). One of the nice features that C# still provides is the familiar try/catch/finally construct, which curiously is still absent from C++. Oh, there are the neigh sayers who will argue that RAII (Resource acquisition is initialization) idiom should obviate the need for a finally { } construct, and that might be true for new development. I would argue that for support existing code, third party code or code that is not easily wrapped in a simple RAII type class object, the finally construct is still quite a useful tool to have in one's toolkit.

Here's the problem... finally is not a part of the C++ language, and as such, is not natively supported. There is __try / __finally compiler support, but you will quickly find that you cant mix native __try / __finally with compiler supported try / catch.

This came about because of a project I was working on, where I had to upgrade the code base from C++08 to C++11. In addition to the desire to add in the try / catch / finally construct, I wanted to be able to handle C++ exceptions, COM HRESULTs, MFC exceptions and IErrorInfo results as well as Win32 error codes. Here is how I had implemented this back in the 2000s:

As you can imagine, this requires typing in the same code repeatedly over and over for every method in which I wanted to implement error handling and logging. Worse, if I wanted to customize the code in anyway, it would require different implementations. Lastly, how was I going to implement the __try / __finally construct?

My first thought was to use stupid preprocessor tricks. It quickly became evident that the preprocessor was not going to address the many permutation I needed to implement this pattern,

Here then, is how I solved this problem.

First step - Lambdas to the rescue

Taking a step back, I thought about how I might solve this problem in the .NET C# domain. The trick is to take the pattern above, and to call out to the caller what is executed inside of the try { } construct. In C#, that would be accomplished with Action<...> as follows:

























We would execute like so:









Lambdas in C++ are a bit of a funny beast. Where as in C#, lambdas are just defined:

( args ) = > { code to exec }

in C++, lambdas are much more powerful.

A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope. We can capture external variables from enclosing scope by three ways :
      Capture by reference
      Capture by value
      Capture by both (mixed capture)
Syntax used for capturing variables :
      [&] : capture all external variable by reference
      [=] : capture all external variable by value
      [a, &b] : capture a by value and b by reference

A lambda with empty capture clause [ ] can access only those variable which are local to it.
We can rewrite our ExecFunc method in C++ as follows:































Second step - Adding try / finally support

The next problem to tackle is the incompatibility when mixing in __try / __finally and try / catch. This is actually much simpler upon reflection... all we need to do is to separate out the __try / __finally part from the rest of the pattern. We can do this by putting the __try / __finally into its own translation unit, and by turning off C++ exception support. It would look something like this:

























We just need to make sure to turn off C++ exceptions for NoUnwind.cpp:



Now the implementation becomes the following:





















Note: Sample source can be found in my .git repository here:
Sample Source


Comments

Popular posts from this blog

Representing C/C++ unions and bitfields in C#

Dispatch? I'd like to make a call. The difference between Synchronization Context, Task Scheduler and Dispatcher