代码之家  ›  专栏  ›  技术社区  ›  ronag

C++ 11范围退出保护,好主意?

  •  24
  • ronag  · 技术社区  · 14 年前

    我为C++ 11编写了一个小实用程序类,它用作一个范围保护器,用于更容易地处理异常安全和类似的事情。

    看起来有点像黑客。但我很惊讶,我还没有看到它在其他地方使用C++ 11的特点。我认为Boost有类似的C++ 98。

    但这是个好主意吗?还是有我遗漏的潜在问题?在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)
    

    它的用途类似于。

    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;
    }
    
    9 回复  |  直到 7 年前
        1
  •  19
  •   dirkgently    14 年前

    但这是个好主意吗?

    当然。相关主题是 RAII paradigm .

    还是那里 我错过的潜在问题?

    您不处理异常。

    是 已经有了类似的解决方案 C++0X特性在升压或类似中?

    亚历山德里斯库想出了 ScopeGuard 很久以前了。Boost和Boost std::tr1 有件事叫 scoped_ptr shared_ptr (使用自定义删除程序)允许您完成这一点。

        2
  •  17
  •   Gregory Pakosz    14 年前

    据记载,有 Boost ScopeExit .

        3
  •  9
  •   deft_code    14 年前

    范围警卫绝对是个好主意。我认为范围保护概念是实现异常安全的有效工具。如果你能做一个更安全更干净的版本 ScopeExit 使用C++ 0x语法,我认为这很值得你花时间。

    类似于亚历山德里斯科的 ScopeGuard 还有Boost的Scopeexit, D programming language 对于这类事情有直接的语法。D编程团队认为Scope Guard是一个很好的主意,他们添加了它 directly to the language (即它没有在库中实现)。

    例子。

    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!");
    }
    

    基于范围的守卫并不是什么新鲜事。它的功能可以很容易地用类析构函数(raii等)复制。也可以用 try/finally 在C语言或Java语言中。甚至pthreads都提供了一个基本的范围保护,称为 pthread_cleanup_push .

    当你有多个 scope(*) 函数中的语句。它的伸缩性非常好,而不是 尝试/最终 这需要超人的力量来管理任何超过两个的事情。

        4
  •  5
  •   Abyx    14 年前

    如果用二元运算符替换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));
        }
    };
    

    用途:

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

    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__]
    
        5
  •  0
  •   Flexo - Save the data dump sunny moon    13 年前

    使用 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));
    }
    
        6
  •  0
  •   bobef    12 年前

    我们可以通过将丑陋的东西定义为:

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

    然后:

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

    使用MSVC++11.0(VS2012)进行测试。当做。

        7
  •  0
  •   Community CDub    8 年前

    这是个好主意,但是你的课有几个问题。

    1. 您应该禁用这个新的操作符(您不想让用户以强制调用delete的方式使用它,对吗?)
    2. 您需要一个“commit”函数,以便 scope guard 而不是简单的RAII

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

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

        8
  •  0
  •   Steve Fan    7 年前

    使用Booost:

    #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 });
    

    用途:

    if (gdiplus::GdiplusStartup(&token, &startupInput, nullptr) == Gdiplus::Ok) {
        defer({
            gdiplus::GdiplusShutdown(token);
        });
        ...
    }
    
        9
  •  -1
  •   nate    13 年前

    我的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; } );