解释了Golang风格defer
的一般概念here和here。
我想知道,STL(C ++ 11,C ++ 14,...)或者Boost或者其他一些库是否包含这样一个类的实现?所以我可以在没有在每个新项目中重新实现它的情况下使用它。
答案 0 :(得分:8)
与其他一些答案不同,这种实现是零开销,语法更好,更易于使用。它也没有依赖关系,减少了编译时间。
您可以将此代码段粘贴到代码库中的任何位置,它就可以正常工作。
#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; };
示例:
#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;
}
}
PS:本地deferrer对象以zz_
而不是_
开头,因此它不会使调试器中的Locals窗口混乱,也因为用户标识符在技术上不应以下划线开头。< / p>
答案 1 :(得分:7)
有proposal for std::unique_resource_t
可启用
auto file=make_unique_resource(::fopen(filename.c_str(),"w"),&::fclose);
表示资源,它定义了一般scope_exit
,它应与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.
答案 2 :(得分:5)
我在CppCon 2014(YouTube link)中展示了Go-style defer
的头文件实现;我叫它Auto
。恕我直言,在可教性,效率和绝对的万无一失方面,这仍然是最好的选择。在使用中,它看起来像这样:
#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看起来像这样:
#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在Auto
或更高级别-O2
使用A B C D E F
Nima1 1 2 3 4 5
Nima2 6 7 8 9 10
Nima3 11 12 13 14 15
Nima4 16 17 18 19 20
时使用完美代码以外的其他内容 - 它是其中一个传说中的#34} ;零成本抽象。&#34;
The original source还有一个C89版本,可以通过利用一些非常特定于GCC的属性在GCC上运行。
答案 3 :(得分:1)
这是我的解决方案,类似于你在swift中遇到的类型,但是我没有处理任何异常(如果需要,只需使用像PSIAlt解决方案中那样的try / catch块就足够了):< / p>
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();
}
}
};
由于使用了向量,它看起来很笨拙,但它保留了swift延迟的行为,其中函数以相反的顺序调用:
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;
但它比推迟更强大,因为你可以在低于你自己的范围内添加延迟调用。你必须要小心,当你使用它时,捕获的lambda不会超出范围(如果你小心的话,有点瑕疵但不是严重的瑕疵)。
我通常不会使用它,除非它是一次性声明。理想情况下,您将围绕一个新类包装任何资源,当它超出范围时将其解除分配,但如果它的顶级代码具有大量资源和错误处理,那么从代码可读性的角度来看,延迟确实更有意义。你不必记住一堆真正做同样事情的新类。
答案 4 :(得分:1)
libgolang提供了 defer 实现(commit 1,2,3)。用法就像:
void myfunc() {
defer([]() {
printf("leaving...\n");
});
...
}
defer
本身实现为:
// 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
};
答案 5 :(得分:0)
在新标准出台之前,我使用简单的RAII类:
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;
};
稍后它可以用于任何事情,例如:
FILE *fp = fopen(filename.c_str(),"w");
if(fp==NULL) throw invalid_argument();
ScopeGuard _fp_guard([&fp]() {
fclose(fp);
});
此外,您可以使用Boost.ScopeExit和类似的实现。
答案 6 :(得分:0)
这是我的延迟实施,但没有noexcept保证,我仍然认为这是非常好的实施。
像这样使用:
#include <iostream>
#include "defer.hpp"
using namespace std;
int main() {
defer []{cout << "defered" << endl;};
}
实现:
#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_;
};
答案 7 :(得分:0)
我们不应该在这里使用 std::function,因为创建 std::function 的对象相当慢。
Here 是 C++11 中没有 std::function 的实现。
#include <utility>
template <typename F>
struct _defer_class {
_defer_class(F&& f) : _f(std::move(f)) {}
~_defer_class() { _f(); }
F _f;
};
template <typename F>
inline _defer_class<F> _create_defer_class(F&& f) {
return _defer_class<F>(std::move(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; })
我们可以这样使用它:
#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;
}
答案 8 :(得分:-1)
像这样使用:
int main() {
int age = 20;
DEFER { std::cout << "age = " << age << std::endl; };
DEFER { std::cout << "I'll be first\n"; };
}
我的实现(请自行添加头文件):
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) = [&] ()