Does C++ support 'finally' blocks? (And what's this 'RAII' I keep hearing about?)

前端 未结 16 1573
情深已故
情深已故 2020-11-22 17:15

Does C++ support \'finally\' blocks?

What is the RAII idiom?

What is the difference between C++\'s RAII idiom and C#\'s \'using\' statement?

16条回答
  •  情歌与酒
    2020-11-22 17:44

    I came up with a finally macro that can be used almost like¹ the finally keyword in Java; it makes use of std::exception_ptr and friends, lambda functions and std::promise, so it requires C++11 or above; it also makes use of the compound statement expression GCC extension, which is also supported by clang.

    WARNING: an earlier version of this answer used a different implementation of the concept with many more limitations.

    First, let's define a helper class.

    #include 
    
    template 
    class FinallyHelper {
        template  struct TypeWrapper {};
        using Return = typename std::result_of::type;
    
    public:    
        FinallyHelper(Fun body) {
            try {
                execute(TypeWrapper(), body);
            }
            catch(...) {
                m_promise.set_exception(std::current_exception());
            }
        }
    
        Return get() {
            return m_promise.get_future().get();
        }
    
    private:
        template 
        void execute(T, Fun body) {
            m_promise.set_value(body());
        }
    
        void execute(TypeWrapper, Fun body) {
            body();
        }
    
        std::promise m_promise;
    };
    
    template 
    FinallyHelper make_finally_helper(Fun body) {
        return FinallyHelper(body);
    }
    

    Then there's the actual macro.

    #define try_with_finally for(auto __finally_helper = make_finally_helper([&] { try 
    #define finally });                         \
            true;                               \
            ({return __finally_helper.get();})) \
    /***/
    

    It can be used like this:

    void test() {
        try_with_finally {
            raise_exception();
        }    
    
        catch(const my_exception1&) {
            /*...*/
        }
    
        catch(const my_exception2&) {
            /*...*/
        }
    
        finally {
            clean_it_all_up();
        }    
    }
    

    The use of std::promise makes it very easy to implement, but it probably also introduces quite a bit of unneeded overhead which could be avoided by reimplementing only the needed functionalities from std::promise.


    ¹ CAVEAT: there are a few things that don't work quite like the java version of finally. Off the top of my head:

    1. it's not possible to break from an outer loop with the break statement from within the try and catch()'s blocks, since they live within a lambda function;
    2. there must be at least one catch() block after the try: it's a C++ requirement;
    3. if the function has a return value other than void but there's no return within the try and catch()'s blocks, compilation will fail because the finally macro will expand to code that will want to return a void. This could be, err, avoided by having a finally_noreturn macro of sorts.

    All in all, I don't know if I'd ever use this stuff myself, but it was fun playing with it. :)

提交回复
热议问题