Go has a neat keyword called defer that is used to ensure that a function call is performed later in a program’s execution, usually for purposes of cleanup.
Suppose we wanted to create a file, write to it, and then close when we’re done:
package main |
Immediately after getting a file object with createFile, we defer the closing of that file with closeFile. This will be executed at the end of the enclosing function (main), after writeFile has finished.
Running the program confirms that the file is closed after being written:
$ go run defer.go |
[!] The above was taken from Go by Example
Implementing defer in C++
C++ has a neat feature called Resource acquisition is initialization, a.k.a RAII. There are a lot of resources online that explain what is RAII and how it works, Tom Dalling’s for example.
One of the top uses for RAII are scope guards, which are usually used to perform cleanup. The concept is explained thoroughly in Generic: Change the Way You Write Exception-Safe Code — Forever.
I didn’t like the implementation they suggested, and instead went searching for a better one. I found what I was looking for on stackoverflow:
class ScopeGuard { |
which can be used as follows:
std::cout << "creating" << std::endl; |
The above execution flow would be: creating -> writing -> closing
.
Nice, right? but it also forces us to name each ScopeGuard, which is annoying.
Thank god we have macros! (never say that. same for goto
) -
and now we have a defer like behaviour in C++:
std::cout << "creating" << std::endl; |
But why do we need the excess [&]() { ... ; }
part? and what is it anyway?[&]
tells the compiler to pass all locals by reference, and ()
is used to indicate function args.
We want this behaviour for all DEFER
calls, so let’s put it in the macro:
And now there’s no need for boilerplate code:
std::ofstream f("/path/to/file"); |
The neat part is that we can call DEFER multiple times without having to name variables,
because each DEFER call creates a ScopeGuard with a random name in order to avoiding colissions;
std::ofstream f1("/path/to/file1"); |
It also works with multiline functions, just like golang’s defer
keyword:
std::ofstream f("/path/to/file1"); |