tonetheman's blog

defer in C++ wut!

defer in C++

In a random hnews comment section I saw some amazing code. See here for the hnews article https://news.ycombinator.com/item?id=32507659

The code showed how to do a golang style defer in C++ using the preprocessor plus some cool C++ class stuff.

Yup this is some odd and amazing code to me.

It is abusing some preprocessor macros a bit but it is still cool.

The code is here

template <typename F>
struct privDefer {
    F f;
    privDefer(F f) : f(f) { }
    ~privDefer() {
        f();
    }
};

template <typename F>
privDefer<F> defer_func(F f) {
    return privDefer<F>(f);
}

#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
#define defer(code) auto DEFER_3(_defer_) = defer_func([&](){code;})


int main() {

    printf("before defer is called...\n");

    defer( printf("hey hey this is defered...\n") );

    printf("after defer is defined...\n");

    printf("about to return from main\n");

    return 0;
}

The concept works mainly by destructor and scopes. Meaning if you have an object that is created at the start of a scope on the stack then at scope exit the destructor will be called.

{
    SomeObject so;
    // do some thing

    // at this point
    // ~so() will be called
}

In the code the destructor call is used to call the function that was saved when the instance was created.

template <typename F>
struct privDefer {
    F f;
    privDefer(F f) : f(f) { }
    ~privDefer() {
        f();
    }
};

So the variable f is passed in the constructor and saved as data in the struct. And when the destructor is called the variable is called like it is a function.

The other thing that confused me about that code was the ## in the preprocessor code.

See here for a description (the microsoft doc is really great!) https://docs.microsoft.com/en-us/cpp/preprocessor/token-pasting-operator-hash-hash?view=msvc-170 https://www.cprogramming.com/reference/preprocessor/token-pasting-operator.html

A great trick to see what happens to the code after the preprocessor is this

g++ -E main.cpp -o main.i

If you run that command you will get a LARGE file main.i that is after the preprocessor has done it's work.

In my example this is what the preprocessor did

    auto _defer_0 = defer_func([&](){printf("hey hey this is defered...\n");});

The other bit to notice is the ___COUNTER___ mention. It is a self incrementing variable that at least gcc makes available for you to use. And in this case it is used to make unique variable names so that you could have multiple defers.

See here: https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

C++ plus the preprocessor can be amazing and scary all at once.