C++11 范围出口保护,一个好主意?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3669833/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:26:16  来源:igfitidea点击:

C++11 scope exit guard, a good idea?

c++c++11

提问by ronag

I've written a small utility class for C++11 which I use as a scope guard for easier handling of exception safety and similar things.

我为 C++11 编写了一个小的实用程序类,我将其用作范围保护,以便更轻松地处理异常安全和类似的事情。

Seems somewhat like a hack. But I'm suprised I haven't seen it somewhere else using C++11 features. I think boost has something similar for C++98.

看起来有点像黑客。但令我惊讶的是我没有在其他地方看到它使用 C++11 特性。我认为 boost 对 C++98 有类似的东西。

But is it a good idea? Or are there potential problems I have missed? Is there already a similar solution (with C++11 features) in boost or similar?

但这是个好主意吗?还是我遗漏了潜在的问题?在 boost 或类似中是否已经有类似的解决方案(具有 C++11 特性)?

    namespace detail 
    {
        template<typename T>
        class scope_exit : boost::noncopyable
        {
        public:         
            explicit scope_exit(T&& exitScope) : exitScope_(std::forward<T>(exitScope)){}
            ~scope_exit(){try{exitScope_();}catch(...){}}
        private:
            T exitScope_;
        };          

        template <typename T>
        scope_exit<T> create_scope_exit(T&& exitScope)
        {
            return scope_exit<T>(std::forward<T>(exitScope));
        }
    }


#define _UTILITY_EXIT_SCOPE_LINENAME_CAT(name, line) name##line
#define _UTILITY_EXIT_SCOPE_LINENAME(name, line) _UTILITY_EXIT_SCOPE_LINENAME_CAT(name, line)
#define UTILITY_SCOPE_EXIT(f) const auto& _UTILITY_EXIT_SCOPE_LINENAME(EXIT, __LINE__) = ::detail::create_scope_exit(f)

and it's used something like.

它使用了类似的东西。

int main () 
{
  ofstream myfile;
  myfile.open ("example.txt");
  UTILITY_SCOPE_EXIT([&]{myfile.close();}); // Make sure to close file even in case of exception
  myfile << "Writing this to a file.\n"; // Imagine this could throw
  return 0;
}

采纳答案by dirkgently

But is it a good idea?

但这是个好主意吗?

Sure. A related topic is the RAII paradigm.

当然。一个相关的主题是RAII 范式

Or are there potential problems I have missed?

还是我遗漏了潜在的问题?

You don't handle exceptions.

你不处理异常。

Is there already a similar solution (with C++0x features) in boost or similar?

在 boost 或类似中是否已经有类似的解决方案(具有 C++0x 功能)?

Alexandrescu came up with ScopeGuarda long time back. Both Boost and std::tr1has a thing called scoped_ptrand shared_ptr(with a custom deleter) that allows you to accomplish just this.

Alexandrescu很久以前就提出了ScopeGuard。Boost 和std::tr1都有一个叫做scoped_ptrand shared_ptr(带有自定义删除器)的东西,可以让你完成这个。

回答by Gregory Pakosz

For the record, there is Boost ScopeExit.

作为记录,有Boost ScopeExit

回答by deft_code

Scope guards are definitely a good idea. I think the scope guard concept is potent tool for exception safety. If you can make a safer, cleaner version that Boost's ScopeExitusing C++0x syntax, I think it would be well worth your time.

范围守卫绝对是一个好主意。我认为范围保护概念是异常安全的有力工具。如果您可以使用 C++0x 语法制作一个更安全、更干净的 Boost ScopeExit版本,我认为这将非常值得您花时间。

Similar to Alexandrescu's ScopeGuardand Boost's ScopeExit , the D programming languagehas direct syntax for this sort of thing. The D programming team thought the scope guard was a good enough idea that they added it directly to the language(ie it's not implemented in a library).

与 Alexandrescu 的ScopeGuard和 Boost 的 ScopeExit 类似,D 编程语言对这类事情有直接的语法。D 编程团队认为范围保护是一个很好的主意,因此他们将它直接添加到语言中(即它没有在库中实现)。

Example.

例子。

void foo( bool fail )
{
   scope(exit)
   {
      writeln("I'm always printed");
   }

   scope(success) writeln("The function exited normally");

   scope(error)
      writeln("The function exited with an exception.");

   if( fail )
      throw new Exception("Die Die Die!");
}

The scope based guards aren't anything new. It's functionality can easily be replicated with a class destructor (RAII and all that). It's also possible to replace with try/finallyin C# or Java. Heck, even pthreads provides a rudimentary scope guard, called pthread_cleanup_push.

基于范围的守卫并不是什么新鲜事。它的功能可以通过类析构函数(RAII 和所有这些)轻松复制。也可以用try/finallyC# 或 Java替换。哎呀,甚至 pthreads 也提供了一个基本的范围保护,称为pthread_cleanup_push

What makes scope guards so powerful is when you have multiple scope(*)statements in the function. It scales incredibly well, as opposed to try/finallywhich require super human powers to manage anything more than two.

作用域守卫如此强大的原因在于scope(*)函数中有多个语句。它的扩展性非常好,而不是try/finally需要超人的力量来管理两个以上的东西。

回答by Abyx

If replace create_scope_exit by a binary operator, we can remove parentheses:

如果用二元运算符替换 create_scope_exit ,我们可以删除括号:

class at_scope_exit
{
    template<typename F>
    struct scope_exit_fn_holder : boost::noncopyable
    {
        scope_exit_fn_holder(F&& f) : f(std::forward<F>(f)) {}

        F f;
        ~scope_exit_fn_holder() { f(); }
    };

    template<typename F>
    friend scope_exit_fn_holder<F> operator==(at_scope_exit, F&& f)
    {
        return scope_exit_fn_holder<F>(std::forward<F>(f));
    }
};

Usage:

用法:

auto atScopeExit = at_scope_exit() == [&]
{
    ...
};

upd:
Corresponding macro:

upd:
对应的宏:

#include <boost/preprocessor/cat.hpp>

#define AT_SCOPE_EXIT auto BOOST_PP_CAT(scopeExit_, __LINE__) = at_scope_exit() == [&]
#define AT_SCOPE_EXIT_EX(...) auto BOOST_PP_CAT(scopeExit_, __LINE__) = at_scope_exit() == [__VA_ARGS__]

回答by Weipeng L

The implementation could be very much simplified using tr1::functionand tr1::unique_ptr, as below:

使用tr1::function和可以非常简化实现tr1::unique_ptr,如下所示:

namespace detail
{
    class ScopeGuard
    {
    public:
        explicit ScopeGuard(std::function<void()> onExitScope) 
            : onExitScope_(onExitScope), dismissed_(false)
        { }

        ~ScopeGuard()
        {
            try
            {
                if(!dismissed_)
                {
                    onExitScope_();
                }
            }
            catch(...){}
        }

        void Dismiss()
        {
            dismissed_ = true;
        }
    private:
        std::function<void()> onExitScope_;
        bool dismissed_;

        // noncopyable
    private:
        ScopeGuard(ScopeGuard const&);
        ScopeGuard& operator=(ScopeGuard const&);
    };
}

inline std::unique_ptr<detail::ScopeGuard> CreateScopeGuard(std::function<void()> onExitScope)
{
    return std::unique_ptr<detail::ScopeGuard>(new detail::ScopeGuard(onExitScope));
}

回答by bobef

We could omit the ugly [&] stuff by putting it in the define:

我们可以通过将它放在定义中来省略丑陋的 [&] 内容:

#define UTILITY_SCOPE_EXIT(f) const auto& _UTILITY_EXIT_SCOPE_LINENAME(EXIT, __LINE__) = ::detail::create_scope_exit([&]f)

Then:

然后:

UTILITY_SCOPE_EXIT({myfile.close();});

Tested with MSVC++ 11.0 (VS2012). Regards.

使用 MSVC++ 11.0 (VS2012) 进行测试。问候。

回答by Stefano Falasca

This is a good idea, but there are a couple of problems with you class.

这是个好主意,但是您的课程存在一些问题。

  1. you should disable the new operator (you don't want to need the user to use it in such a way that forces to call delete on this, right?)
  2. you need a "commit" function, in order for this to be a scope guardinstead of a simple RAII
  1. 您应该禁用 new 运算符(您不希望用户以强制调用 delete 的方式使用它,对吗?)
  2. 你需要一个“提交”功能,以便它成为一个范围守卫而不是一个简单的 RAII

notice that if you implement point 2 you need a meaningful name for each scopeguard you instantiate. This is, in general, not a problem, but it could be in your application (or to your taste).

请注意,如果您实现第 2 点,您需要为您实例化的每个 scopeguard 一个有意义的名称。一般来说,这不是问题,但它可能存在于您的应用程序中(或符合您的口味)。

Finally, this question would probably have been more appropriate for CodeReview.

最后,这个问题可能更适合CodeReview

回答by Steve Fan

Using Boost:

使用提升:

#include <boost/preprocessor/cat.hpp>

template<class Fn>
class ScopeGuardDetails {
    const Fn m_fn;
public:
    constexpr ScopeGuardDetails(Fn &&fn) : m_fn(fn) {}
    ~ScopeGuardDetails() { m_fn(); }
};
#define ScopeGuardName BOOST_PP_CAT(BOOST_PP_CAT(__scope_guard, _), BOOST_PP_CAT(BOOST_PP_CAT(__LINE__, _), __COUNTER__))
#define defer(stmt) const auto ScopeGuardName = [](const auto _fn) { \
    return ScopeGuardDetails<decltype(_fn)> { std::move(_fn) }; \
}([&] { stmt });

Usage:

用法:

if (gdiplus::GdiplusStartup(&token, &startupInput, nullptr) == Gdiplus::Ok) {
    defer({
        gdiplus::GdiplusShutdown(token);
    });
    ...
}

回答by nate

my $0.02

我的 0.02 美元

struct at_scope_end
{
    std::function < void () > Action;

    at_scope_end (std::function < void () > Action) :
        Action (Action)
    {
    }

    ~at_scope_end ()
    {
        Action ();
    }
};

#define AT_SCOPE_END_CAT(x,y)    x##y
#define AT_SCOPE_END_ID(index)   AT_SCOPE_END_CAT(__sg, index)
#define AT_SCOPE_END(expr)      at_scope_end AT_SCOPE_END_ID(__LINE__) ( [&] () { expr; } );