首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C++中标准的延迟/终结器实现是什么?

在C++中标准的延迟/终结器实现是什么?
EN

Stack Overflow用户
提问于 2015-09-07 06:35:25
回答 9查看 5.3K关注 0票数 11

介绍了Golang风格defer的一般思想,阐述了这里这里.

我想知道,( C++11,C++14,.)或者可能是Boost或者其他库包含了这样一个类的实现?所以我可以不用在每一个新项目中重新实现它。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2015-09-07 07:10:08

有一个std::unique_resource_t将启用类似的代码

代码语言:javascript
复制
auto file=make_unique_resource(::fopen(filename.c_str(),"w"),&::fclose);

对于资源,它定义了一个通用的scope_exit,它应该与defer相同

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

它将被考虑在后C++17标准中采用。

票数 10
EN

Stack Overflow用户

发布于 2017-02-06 03:58:44

这个实现是零开销,不像其他一些答案,以及更好的语法和更容易使用。它还具有零依赖项,减少了编译时间。

您可以在代码库中的任何地方粘贴此代码段,它将只起作用。

代码语言:javascript
复制
#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

用法:defer { statements; };

示例:

代码语言:javascript
复制
#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.:本地延迟器对象以zz_而不是_开头,因此它不会干扰调试器中的局部变量窗口,还因为用户标识符在技术上不应该以下划线开头。

票数 20
EN

Stack Overflow用户

发布于 2016-07-10 06:08:37

我在2014年的defer (YouTube链路)上介绍了一个纯头的Go风格的实现,我称之为Auto。从可教性、效率和绝对的抗愚昧性来看,这仍然是最好的选择。在使用中,它看起来如下:

代码语言:javascript
复制
#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]; }
}

实施看起来是这样的:

代码语言:javascript
复制
#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__)

是的,这是整个文件:只有15行代码!它需要C++11或更新版本,并要求编译器支持__COUNTER__ (尽管如果需要移植到某个不支持它的编译器,则可以将__LINE__用作穷人的__COUNTER__ )。至于效率,我从未见过GCC或Clang为-O2或更高版本的任何-O2使用生成完美的代码--这是传说中的“零成本抽象”之一。

原源也有一个C89版本,它可以利用GCC特有的一些特定属性来处理GCC的问题。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32432450

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档