c++中标准的延迟/终结器实现是什么?
What is standard defer/finalizer implementation in C++?
在这里和这里解释了Golang-style defer
的总体思想。
我想知道,STL (c++ 11, c++ 14,…)或Boost或其他一些库包含这样一个类的实现吗?所以我可以直接使用它而不用在每个新项目中重新实现它
与其他答案不同,这个实现的开销为零,而且在语法上更好,更易于使用。它也没有依赖,减少了编译时间。
你可以把这段代码粘贴到你的代码库的任何地方,它就会正常工作。
#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;
}
}
注::本地延迟对象以zz_
开始,而不是_
,所以它不会使调试器中的Locals窗口混乱,也因为用户标识符在技术上不应该以下划线开头。
有一个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;
});
将考虑在后c++ 17标准中可能采用
我在CppCon 2014 (YouTube链接)上展示了go风格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]; }
}
实现如下:
#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
或更高级别使用Auto
时生成的代码不是完美的——这是传说中的"零成本抽象"之一。
原始源代码还有一个C89版本,它通过利用一些非常特定于GCC的属性在GCC上工作。
这是我的解决方案,它类似于您在swift中遇到的类型,但我不处理任何异常(如果需要,只需使用try/catch块,如PSIAlt的解决方案):
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();
}
}
};
由于使用了vector,它可能看起来很笨拙,但它保留了swift的defer的行为,函数以相反的顺序调用:
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;
但是它比defer更强大一点,因为你可以在任何低于你自己的作用域添加延迟调用。你只需要小心,当你使用这个时,捕获的lambda不会超出作用域(有点缺陷,但如果你小心的话,不是一个严重的缺陷)。
我通常不会使用这个,除非它是一个一次性语句。理想情况下,您应该将任何资源包装在一个新类周围,当它超出作用域时释放它,但是如果它的顶级代码包含大量资源和错误处理,那么从代码可读性的角度来看,defer确实更有意义。你不需要记住一堆做同样事情的新类
libgolang提供defer实现(提交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
};
在新标准到来之前,我使用简单的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和类似的实现
这是我的延迟实现,但没有除了保证,我仍然认为它不是很好的实现。
像这样使用:
#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_;
};
我们不应该在这里使用std::function,因为创建std::function的对象相当慢。
下面是c++ 11中使用lambda代替std::函数的实现。编译器将内联lambda,我们将获得最佳性能。
#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; })
我们可以这样使用:
#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;
}
如下所示:
int main() {
int age = 20;
DEFER { std::cout << "age = " << age << std::endl; };
DEFER { std::cout << "I'll be firstn"; };
}
我的实现(请自己添加头文件):
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) = [&] ()
- 实现无开销push_back的最佳方法是什么
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 是什么原因导致它无法编译?它是声明签名还是在函数本身的实现中
- 私有在函数定义/实现的返回值范围内是什么意思 (c++)?
- 在 c++ 或 python 中生成一个体面的视差图以在 Raspberry Pi 上实现的最佳方法(算法或函数)是什么
- 在 C++17 中实现迭代器和const_iterator的正确方法是什么?
- 十进制到二进制的实现不能完全适用于我大学的检查器。问题或提示可能是什么
- 实现此自定义priority_queue的正确方法是什么
- 实现此"on error, throw"回调的最佳方法是什么?
- 在QGraphicsView上实现交互式样条曲线的最佳方法是什么?
- 当您知道结构将来会更改时,实现结构读写的最佳方法是什么
- std :: is_nothrow_copy_copy_constructible的实现是什么?
- 具有多态数据的容器的常见实现是什么
- RWTPtrOrderedVector的替代实现是什么
- 将一元函数应用于向量的某些元素的良好实现是什么?
- 向量类中擦除函数的正确实现是什么
- 映射到ASCII的Character的底层实现是什么?
- c++中标准的延迟/终结器实现是什么?
- 访问和更改长布尔数组的最快实现是什么?
- 单例类的确切用法和实现是什么?