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.
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 both (mixed capture)
Syntax used for capturing variables :
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
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:
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 valueCapture 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
Post a Comment