12

General idea of Golang-style defer is explained here and here.

I wonder, does STL (of C++11, C++14, ...) or maybe Boost or maybe some other library contain implementation of such a class? So I could just use it without reimplementing it in every new project.

Aleksander Alekseev
  • 1,854
  • 4
  • 25
  • 57
  • 1
    The STL does not. But both those links give small samples (a few lines) of C++11 code to achieve the behaviour your seek, so you would probably spend more time trying to find a library to provide the capability than to roll your own. – Peter Sep 07 '15 at 06:51

9 Answers9

23

This implementation is zero-overhead unlike some other answers, as well as syntactically nicer and easier to use. It also has zero dependencies, reducing compile times.

You can paste this snippet anywhere in your codebase and it will just work.

#ifndef defer
struct defer_dummy {};
template <class F> struct deferrer { F f; ~deferrer() { f(); } };
template <class F> deferrer<F> operator*(defer_dummy, F f) { return {f}; }
#define DEFER_(LINE) zz_defer##LINE
#define DEFER(LINE) DEFER_(LINE)
#define defer auto DEFER(__LINE__) = defer_dummy{} *[&]()
#endif // defer

Usage: defer { statements; };

Example:

#include <cstdint>
#include <cstdio>
#include <cstdlib>

#ifndef defer
struct defer_dummy {};
template <class F> struct deferrer { F f; ~deferrer() { f(); } };
template <class F> deferrer<F> operator*(defer_dummy, F f) { return {f}; }
#define DEFER_(LINE) zz_defer##LINE
#define DEFER(LINE) DEFER_(LINE)
#define defer auto DEFER(__LINE__) = defer_dummy{} *[&]()
#endif // defer

bool read_entire_file(char *filename, std::uint8_t *&data_out,
                      std::size_t *size_out = nullptr) {
    if (!filename)
        return false;

    auto file = std::fopen(filename, "rb");
    if (!file)
        return false;

    defer { std::fclose(file); }; // don't need to write an RAII file wrapper.

    if (std::fseek(file, 0, SEEK_END) != 0)
        return false;

    auto filesize = std::fpos_t{};
    if (std::fgetpos(file, &filesize) != 0 || filesize < 0)
        return false;

    auto checked_filesize = static_cast<std::uintmax_t>(filesize);
    if (checked_filesize > SIZE_MAX)
        return false;

    auto usable_filesize = static_cast<std::size_t>(checked_filesize);
    // Even if allocation or read fails, this info is useful.
    if (size_out)
        *size_out = usable_filesize;

    auto memory_block = new std::uint8_t[usable_filesize];
    data_out = memory_block;
    if (memory_block == nullptr)
        return false;

    std::rewind(file);
    if (std::fread(memory_block, 1, usable_filesize, file) != usable_filesize)
        return false; // Allocation succeeded, but read failed.

    return true;
}

int main(int argc, char **argv) {
    if (argc < 2)
        return -1;

    std::uint8_t *file_data = nullptr;
    std::size_t file_size = 0;

    auto read_success = read_entire_file(argv[1], file_data, &file_size);

    defer { delete[] file_data; }; // don't need to write an RAII string wrapper.

    if (read_success) {
        for (std::size_t i = 0; i < file_size; i += 1)
            std::printf("%c", static_cast<char>(file_data[i]));
        return 0;
    } else {
        return -1;
    }
}

P.S.: The local deferrer object starts with zz_ and not _ so it doesn't clutter the Locals window in your debugger, and also because user identifiers technically shouldn't start with underscores.

pmttavara
  • 698
  • 7
  • 16
  • What's the benefit of having it templated (class F)? It's always `function`. – tamas.kenez Oct 28 '17 at 20:18
  • 3
    The deferred block **takes** no parameters, but **captures** the entire scope by reference. Lambdas with captures can't implicitly convert to, say, `void(*)()`, since the capture necessitates storage. Using `std::function` works, but risks heap allocation given large capture lists. By keeping the capture on the stack, this answer lets the compiler inline the entire block yielding a zero-overhead deferral. – pmttavara Oct 30 '17 at 04:37
  • 1
    Tested on godbolt and you're right. I don't think anything would go to the heap but otherwise using std::function does leave a whole lot of boilerplate there while using template results in zero-overhead. I'm still a bit surprised that C++ has two distinct lambda constructs, std::function and a native, magic one of unspecified type. Would be nice if they were identical. – tamas.kenez Oct 30 '17 at 10:37
  • 1
    [This post](http://the-witness.net/news/2012/11/scopeexit-in-c11/#comment-84111) points out why `std::function` would incur heap allocation. And it's not that C++ has two lambda constructs, it's that lambdas couldn't possibly all have the same type, so the designed-by-committee standard library has a highly inefficient type-erasure construct to generalize function objects. – pmttavara Oct 31 '17 at 18:04
  • Yes, `std::function` heap-allocates, after some reading now it seems trivial, thanks. – tamas.kenez Oct 31 '17 at 21:49
  • I don't know why this isn't regarded as the correct answer? It is delicious, and as close to go as possible. – Robert Nov 25 '17 at 19:20
  • What's the meaning of `struct defer_dummy` ? I know it as parameter of * operator, any other benefit ? – Sterling William Jan 20 '22 at 07:08
  • Is the behaviour of this approach fully defined? The lambda captures the all stack variables by reference. As they are destroyed in reverse order of their definition, the lambda always should reference *not* destroyed objects. Is this sufficient or are there some further traps? – ma-hnln Jul 27 '22 at 10:50
  • @SterlingWilliam - The dummy is needed to allow for an operator overload that does this very specific functionality without risking collision with some other type. I could've written `operator*(int)`, but then every int multiplied with a lambda would suddenly create a deferrer object, which is not what we want! – pmttavara Dec 12 '22 at 19:06
10

There is a proposal for std::unique_resource_t which will enable code like

auto file=make_unique_resource(::fopen(filename.c_str(),"w"),&::fclose);

for resources, and it defines a general scope_exit, which should be the same as defer:

// Always say goodbye before returning,
auto goodbye = make_scope_exit([&out]() ->void
{
out << "Goodbye world..." << std::endl;
});

It will be considered for likely adoption in the Post-C++17 standard.

pmttavara
  • 698
  • 7
  • 16
Jens
  • 9,058
  • 2
  • 26
  • 43
  • 5
    And there's of course `unique_ptr` which already does 90% of the same things; Further more there's Boost [ScopeExit](http://www.boost.org/doc/libs/1_59_0/libs/scope_exit/doc/html/index.html). Can you tell I'm too lazy to actually write the answer :) – sehe Sep 07 '15 at 08:17
  • I was tempted to use unique_ptr as well, but then it would have a dummy pointer which I would want to fill out using a NULL. But if I did that, would it call the deleter? – Adrian Ratnapala May 24 '16 at 08:30
  • @Jens: Bad news; [it looks like scope_exit et. al won't make C++17](http://stackoverflow.com/a/38285263/734069). – Nicol Bolas Jul 09 '16 at 18:58
  • @NicolBolas Yeah, I think I am not the only one who is a little disappointed by the next standard. No concepts, no modules, but at least a bunch of library extensions. I wonder if it would be better to split the library into its own standard. Language tend to evolve slowly, but libraries should evolve faster than a three-year cycle. – Jens Jul 10 '16 at 08:32
  • 2
    @AdrianRatnapala The deleter will _not_ get called when the internal pointer is `nullptr`. – Felix Glas Apr 27 '17 at 16:22
6

I presented a header-only implementation of Go-style defer at CppCon 2014 (YouTube link); I called it Auto. IMHO this is still far and away the best alternative out there in terms of teachability, efficiency, and absolute fool-proofness. In use, it looks like this:

#include "auto.h"

int main(int argc, char **argv)
{
    Auto(std::cout << "Goodbye world" << std::endl);  // defer a single statement...
    int x[4], *p = x;
    Auto(
        if (p != x) {  // ...or a whole block's worth of control flow
            delete p;
        }
    );
    if (argc > 4) { p = new int[argc]; }
}

The implementation looks like this:

#pragma once

template <class Lambda> class AtScopeExit {
  Lambda& m_lambda;
public:
  AtScopeExit(Lambda& action) : m_lambda(action) {}
  ~AtScopeExit() { m_lambda(); }
};

#define Auto_INTERNAL2(lname, aname, ...) \
    auto lname = [&]() { __VA_ARGS__; }; \
    AtScopeExit<decltype(lname)> aname(lname);

#define Auto_TOKENPASTE(x, y) Auto_ ## x ## y

#define Auto_INTERNAL1(ctr, ...) \
    Auto_INTERNAL2(Auto_TOKENPASTE(func_, ctr), \
                   Auto_TOKENPASTE(instance_, ctr), __VA_ARGS__)

#define Auto(...) Auto_INTERNAL1(__COUNTER__, __VA_ARGS__)

Yes, that's the entire file: just 15 lines of code! It requires C++11 or newer, and requires your compiler to support __COUNTER__ (although you can use __LINE__ as a poor man's __COUNTER__ if you need portability to some compiler that doesn't support it). As for efficiency, I've never seen GCC or Clang generate anything other than perfect code for any use of Auto at -O2 or higher — it's one of those fabled "zero-cost abstractions."

The original source also has a C89 version that works on GCC by exploiting some very GCC-specific attributes.

Community
  • 1
  • 1
Quuxplusone
  • 23,928
  • 8
  • 94
  • 159
2

Here's my solution, which is similar to the type you'd encounter in swift, but I don't handle any exceptions (easy enough to add if required, just use a try/catch block like in PSIAlt's solution):

class Defer {
    using F = std::function<void(void)>;
    std::vector<F> funcs;
    void add(F f) {
        funcs.push_back(f);
    }
public:
    Defer(F f) { add(f); }
    Defer() {}
    Defer(const Defer& ) = delete;
    Defer& operator= (const Defer& ) = delete;

    void operator() (F f) { add(f); }
    ~Defer() {
        for(;!funcs.empty();) {
            funcs.back()();
            funcs.pop_back();
        }
    }
};

It may seem clunky due to its use of vector, but it retains the behavior of swift's defer where the functions are called in reverse order:

Defer defer([]{std::cout << "fourth" << std::endl;});
std::cout << "first" << std::endl;
auto s = "third";
defer([&s]{std::cout << s << std::endl;});
std::cout << "second" << std::endl;

But it's a bit more powerful than defer in that you can add deferred calls at any scope lower than your own. You just have to be careful that the lambda captured doesn't go out of scope when you use this (a bit of a flaw but not a serious one if you're careful).

I normally wouldn't use this, unless it's going to be a one-off statement. Ideally you'd wrap any resource around a new class that deallocates it when it goes out of scope, but if its top-level code with lots of resources and error handling, defer does make more sense from a code readability standpoint. You don't have to remember a bunch of new classes that really all do the same thing.

austere
  • 157
  • 7
1

libgolang provides defer implementation (commit 1, 2, 3). Usage is like:

void myfunc() {
      defer([]() {
          printf("leaving...\n");
      });

      ...
}

defer itself is implemented as:

// defer(f) mimics `defer f()` from golang.
// NOTE contrary to Go f is called at end of current scope, not function.
#define defer(f) golang::_deferred _defer_(__COUNTER__) (f)
#define _defer_(counter)    _defer_2(counter)
#define _defer_2(counter)   _defer_##counter
struct _deferred {
    typedef func<void()> F;
    F f;
    _deferred(F f) : f(f) {}
    ~_deferred() { f(); }
private:
    _deferred(const _deferred&);    // don't copy
    _deferred(_deferred&&);         // don't move
};
kirr
  • 391
  • 4
  • 8
0

Before new standard comes, I use simple RAII class for this:

struct ScopeGuard {
    typedef std::function< void() > func_type;
    explicit ScopeGuard(func_type _func) : func(_func), isReleased(false) {}
    ~ScopeGuard() {
        if( !isReleased && func ) try {
            func();
        }catch(...) {};
    }
    void Forget() { isReleased=true; }

    //noncopyable
    ScopeGuard(const ScopeGuard&) = delete;
    ScopeGuard& operator=(const ScopeGuard&) = delete;

private:
    func_type func;
    bool isReleased;
};

Later it can be used for any things, for example:

FILE *fp = fopen(filename.c_str(),"w");
if(fp==NULL) throw invalid_argument();
ScopeGuard _fp_guard([&fp]() {
    fclose(fp);
});

Also, you can use Boost.ScopeExit and similar implementations.

Galimov Albert
  • 7,269
  • 1
  • 24
  • 50
  • 1
    Excuse me, but shouldn't ScopeGuard be bound to any variable in this example? – Aleksander Alekseev Sep 08 '15 at 06:15
  • Sadly, the use of `std::function` here will kill your codegen; you really don't want to do it. See [this presentation](https://www.youtube.com/watch?v=lKG1m2NkANM) starting at 10m58s. – Quuxplusone Aug 07 '16 at 18:36
0

Here is my defer implementation, but without noexcept guarantee, I still don't think it is very good implementation.

Used like this:

#include <iostream>
#include "defer.hpp"

using namespace std;

int main() {
    defer []{cout << "defered" << endl;};
}

Implementation:

#define DEFER_CONCAT_IMPL(x, y) x##y
#define DEFER_CONCAT(x, y) DEFER_CONCAT_IMPL(x, y)
#define AUTO_DEFER_VAR DEFER_CONCAT(__defer, __LINE__)
#define defer ::__defer AUTO_DEFER_VAR; AUTO_DEFER_VAR-

class __defer {
    public:
        template<typename Callable>
            void operator- (Callable&& callable) {
                defer_ = std::forward<Callable>(callable);
            }

        ~__defer() {
            defer_();
        }
    private:
        std::function<void(void)> defer_;
};
phonzia
  • 66
  • 5
0

We should not use std::function here, as creating objects of std::function is rather slow.

Here is an implement in C++11 using lambda instead of std::function. Compilers will inline the lambda and we'll get the best performance.

#include <utility>

template <typename F>
struct _defer_class {
    _defer_class(F&& f) : _f(std::forward<F>(f)) {}
    ~_defer_class() { _f(); }
    typename std::remove_reference<F>::type _f;
};

template <typename F>
inline _defer_class<F> _create_defer_class(F&& f) {
    return _defer_class<F>(std::forward<F>(f));
}

#define _defer_name_cat(x, n) x##n
#define _defer_name(x, n) _defer_name_cat(x, n)
#define _defer_var_name _defer_name(_defer_var_, __LINE__)

#define defer(e) \
    auto _defer_var_name = _create_defer_class([&](){ e; })

We can use it like this:

#include "co/defer.h"
#include "co/log.h"

void f(int x, int y) {
    COUT << (x + y);
}

int main(int argc, char** argv) {
    defer(COUT << "hello world");
    defer(COUT << "hello again");
    defer(f(1, 1); f(1, 3));
    return 0;
}
idealvin
  • 178
  • 1
  • 5
-1

used like this:

int main()   {
    int  age = 20;
    DEFER { std::cout << "age = " << age << std::endl; };
    DEFER { std::cout << "I'll be first\n"; };
}

My implementation( please add header files yourself):

 class ScopeExit
    {
    public:
    ScopeExit() = default;

    template <typename F, typename... Args>
    ScopeExit(F&& f, Args&&... args)
    {
        // Bind all args, make args list empty
        auto temp = std::bind(std::forward<F>(f), std::forward<Args>(args)...);

        // Discard return type, make return type = void, conform to func_ type
        func_ = [temp]() { (void)temp(); };
    }

    ScopeExit(ScopeExit&& r)
    {
        func_ = std::move(r.func_);
    }

    // Destructor and execute defered function
    ~ScopeExit()
    {
        if (func_)
            func_();
    }

    private:
        std::function< void ()> func_;
    };

    // Ugly macro, help
    #define CONCAT(a, b) a##b

    #define DEFER  _MAKE_DEFER_HELPER_(__LINE__)
    #define _MAKE_DEFER_HELPER_(line)   ScopeExit    CONCAT(_defer, line) = [&] () 
Bert Young
  • 21
  • 2
  • Sadly, the use of `std::function` here will kill your codegen; you really don't want to do it. See [this presentation](https://www.youtube.com/watch?v=lKG1m2NkANM) starting at 10m58s. – Quuxplusone Jul 10 '16 at 06:11