Does ScopeGuard use really lead to better code?

前端 未结 8 1892
-上瘾入骨i
-上瘾入骨i 2020-12-04 15:36

I came across this article written by Andrei Alexandrescu and Petru Marginean many years ago, which presents and discusses a utility class called ScopeGuard for writing exce

8条回答
  •  鱼传尺愫
    2020-12-04 16:20

    My experience shows that usage of scoped_guard is far inferior to any of the short reusable RAII classes that you can write by hand.

    Before trying the scoped_guard, I had written RAII classes to

    • set GLcolor or GLwidth back to the original, once I've drawn a shape
    • make sure a file has fclosed once I had fopened it.
    • reset a mouse pointer to its initial state, after I've changed it to gears/hourgrlass during a execution of a slow function
    • reset the sorting state of a QListView's back to its previous state, once I've temporarily finished with altering its QListViewItems -- I did not want the list to reorder itself everytime I changed the text of a single item...

    using simple RAII class

    Here's how my code looked like with my hand-crafted RAII classes:

    class scoped_width {
        int m_old_width;
    public:
        scoped_width(int w) {
            m_old_width = getGLwidth();
            setGLwidth(w);
        }
        ~scoped_width() {
            setGLwidth(m_old_width);
        }
    };
    
    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        auto guard = scoped_width(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_width sets GLwidth back to 1 here
    

    Very simple implementation for scoped_width, and quite reusable. Very simple and readable from the consumer side, also.

    using scoped_guard (C++14)

    Now, with the scoped_guard, I have to capture the existing value in the introducer ([]) in order to pass it to the guard's callback:

    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        auto guard = sg::make_scoped_guard([w=getGLwidth()](){ setGLwidth(w); }); // capture current GLwidth in order to set it back
        setGLwidth(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_guard sets GLwidth back to 1 here
    

    The above doesn't even work on C++11. Not to mention that trying to introduce the state to the lambda this way hurts my eyes.

    using scoped_guard (C++11)

    In C++11 you have to do this:

    void DrawTriangle(Tria *t)
    {
        // GLwidth=1 here
    
        int previous_width = getGLwidth();  // explicitly capture current width 
        auto guard = sg::make_scoped_guard([=](){ setGLwidth(previous_width); }); // pass it to lambda in order to set it back
        setGLwidth(2); // sets GLwidth=2
    
        draw_line(t->a, t->b);
        draw_line(t->b, t->c);
        draw_line(t->c, t->a);
    
        setGLwidth(5);
    
        draw_point(t->a);
        draw_point(t->b);
        draw_point(t->c);
    
    }  // scoped_guard sets GLwidth back to 1 here
    

    As you can see,

    • the scoped_guard snoppet requires

      • 3 lines to keep previous value (state) and set it to a new one, and
      • 2 stack variables (previous_width and guard, again) to hold the previous state
    • the hand-crafted RAII class requires

      • 1 readable line to set new state and keep the previous one, and
      • 1 stack variable (guard) to hold the previous state.

    Conclusion

    I think that examples such as

    void some_function() {
        sg::scoped_guard([](){ cout << "this is printed last"; }
    
        cout << "this is printed first";
    }
    

    are no proof of the usefullness of scoped_guard.

    I hope that somebody can show me why I don't get the expected gain from scoped_guard.

    I am convinced that RAII can be exploited better by writing short hand-crafted classes, than using the more generic but hard to use scoped_guard

提交回复
热议问题