Golang my C++ – Defer

Go!

A month ago I started learning Go, since I can’t learn by doing a ton of simple samples I started implementing a physically based renderer (may be more on this later). While reading about the language features there were a couple that sounded really great. The main one being defer calls.

Defer

The idea behind defer is simple: “execute X at the end of this method”. So wherever the method “ends” you can be sure X will be executed. This sounds simple, but if you have a complex method with multiple return statements, things can get tricky.

Lets illustrate this with an example from [0]:


func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}

view raw

defer.go

hosted with ❤ by GitHub

“Awesome” is the word you are looking for.

In Python’s words

Python has something somewhat similar, which is the with statement. You can read about it in many places, for example [1].

Yeah, it’s far from a defer, but you have a really nice way of creating controlled execution in terms of keeping the state as you want it without much trouble. Either you use the Python implemented classes, like file (with open(blah) as f:), or you can create your own object with __enter__ and __exit__ implemented and you can with’ it all you want.

Show me that C++

Actually, these tricks can be done in Python too, but given the handling of scopes, it might get tricky. So I’ll just keep this C++ for now.

So, I read a couple of days ago about a trick like defer with a std::shared_ptr with a lambda deallocator which acts as a defer call when the object gets deleted at the end of its scope. It might sound nice, and under certain circumstances you might even say it’s reasonable… may be. But I don’t like it very much. You know, this may be one of those “tabs vs spaces for indentation” discussions.

Later on, I read about another implementation that did basically the same as the shared_ptr one but in a more correct way, if I may call it that way. Basically you create an object with a lambda as a parameter, and that gets called in the object’s destructor at the end of its scope. We are almost there.

There is at least one issue here, one of the nice things about Go’s defer is that you can do this:


func b() {
for i := 0; i < 4; i++ {
defer fmt.Print(i)
}
}

view raw

defer2.go

hosted with ❤ by GitHub

and it’ll print 3 2 1 0, because it’ll execute it in LIFO order. But with C++ you cannot do that, because if you declare an object inside a block {}, it’ll get deleted at the end of it, which would be at the end of each loop. So we need some more C++ sugar to try to emulate Go’s defer to the last detail.

Implementation

Why don’t we skip all these words and see some code? Ok.


#include <iostream>
#include <functional>
#include <stack>
class Deferrer
{
public:
Deferrer() {}
~Deferrer() { callAll(); }
void addCall(std::function<void()> &&func)
{
_callStack.push(std::forward<decltype(func)>(func));
}
private:
std::stack<std::function<void()>> _callStack;
void callAll()
{
while(!_callStack.empty())
{
_callStack.top()();
_callStack.pop();
}
}
};
// We might want perfect forwarding here, but that's in the TODO list for now
#define defer(…) \
do { \
auto deferred = std::bind(__VA_ARGS__); \
__deferrer.addCall(deferred); \
} while(0);
#define allow_deferred() \
Deferrer __deferrer;
class A
{
public:
A() {}
~A() { std::cout << "Finishing A…" << std::endl; }
void f() { std::cout << "Inside A::f" << std::endl; }
};
void func(int &val)
{
A a;
// the allow_deferred call should be after all local variable declarations
allow_deferred();
defer(&A::f, &a);
val = 1;
for(int i = 0; i < 4; i++)
{
defer([=]() { std::cout << i << std::endl; });
}
defer([&val]() { val = 42; });
}
int main()
{
int i = 0;
func(i);
std::cout << "i = " << i << std::endl;
return 0;
}

view raw

defer.cpp

hosted with ❤ by GitHub

Something worth mentioning regarding this implementation is that you need to run allow_deferred() after all the objects you want to defer a call, otherwise you might get into a problem. A solution would be to use std::shared_ptr, but that depends on the method you are working on.

Other than that, it would be nice to avoid macros but given the approach I’ve taken, it is not possible. A solution would be to have a Deferrer singleton that handles “temporal marks” so you mark it when you run allow_deferred() with the current time and the deferrer will execute the methods until a mark is found. This has two pitfalls, the first is that you’ll need to also call a end_deferrer() at the end of the method, which would defeat the purpose of handling multiple return methods (and obviously forgetting about calling something at the end). And the other problem is that you will need one deferrer per thread (or something equivalent) if you are running in a multithreaded app. So, all in all, I think this approach is a good compromise.

Oh yeah, here’s the output:
3
2
1
0
Inside A::f
Finishing A…
i = 42

Is this a complete solution?

According to [0], there a couple of things that defer does:

A deferred function’s arguments are evaluated when the defer statement is evaluated

This is not true if you bind a parameter, but it is if you use a lambda. So, we have more flexibility but we have to be carefull, as usual.

Deferred function calls are executed in Last In First Out order after the surrounding function

This is true, as we’ve seen from the example. Not much more to say.

Deferred functions may read and assign to the returning function’s named return values

Kind of true, C++ doesn’t have named return values, but anything passed as reference (or a pointer for that matter) can be modifyable if treated properly in the deferred call (lambda with capture by reference, or copy if handling std::shared_ptr).

References

[0] http://blog.golang.org/2010/08/defer-panic-and-recover.html
[1] http://effbot.org/zone/python-with-statement.htm

3 thoughts on “Golang my C++ – Defer

    1. I’ve had that talk in my to-see list for a while now, I’ll bump its priority right away.
      This is a special case of a ScopeGuard, so it depends on what you want to accomplish.

      Why do you say that having the func in heap is a bad thing? I understand that it would be problematic in extreme error situations, but in those cases may be you need some kind of google breakpad behavior, since the env would be really delicate.

      Thanks for the links!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s