accident 994009 1280

C++ Core Guidelines: finally in C++

To make my point clear, this post is about the exceptional case that you can not throw an exception. If your program runs in a restricted embedded environment or you have to fulfil a hard-real-time requirement, this situation may be not so exceptional to you.

 accident 994009 1280

Let’s start with the exceptional environment in which you can’t throw exceptions. My original plan was to write at least about the rules E.19 to E.27. But I get stuck at rule E.19.

E.19: Use a final_action object to express cleanup if no suitable resource handle is available

The first rule may surprise you because you have never heard about final_action. Me too. Therefore I researched. During my research, I found an excellent post on this rule by Bartłomiej Filipek. Bartłomiej Filipek is the author of the well-known C++-blog: Bartek’s coding blog. With his permission, I’m happy to incorporate his post Beautiful code: final_act from GSL into my post. Here we are.

Sometimes there’s a need to invoke a particular action at the end of the scope: it could be a resource releasing code, flag set, code guard, begin/end function calls, etc. Recently, I’ve found a beautiful utility that helps in that cases.
Let’s meet gsl::final_act/finally.

Intro

Follow-up post here: link.

Imagine we have the following code:

void addExtraNodes();
void removeExtraNodes();

bool Scanner::scanNodes()
{
    // code...
    addExtraNodes();

    // code...
    removeExtraNodes();
    return true;
}

 

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

Be part of my mentoring programs:

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (starts March 2024)
  • Do you want to stay informed: Subscribe.

     

    We have a bunch of objects that scanNodes scans (global or shared container), but we need to add some extra nodes to check. We want to preserve the initial container state, so in the end, we must remove those additional nodes.

    Of course, the design of the whole scan code could be much better so that we work on a copy of the container, and adding or removing extra stuff would not be a problem. But there are places, especially in legacy code, where you work on some global container, and special care needs to be taken when changing it. Many bugs can happen when you modify a state and someone expects a different state of the shared container.

    My code seems to be working as expected… right? I call removeExtraNodes at the end of the function.

    But what if there are multiple returns from scanNodes? It’s simple: we need to add multiple calls to removeExtraNodes. Ok….

    What if there are some exceptions thrown? Then we also need to call our cleanup function before we throw…

    So it appears we need to call removeExtraNodes not only before the last return!

    Help needed

    Let’s look at the C++ Core Guidelines. They suggest doing the following thing:

    E.19: Use a final_action object to express cleanup if no suitable resource handle is available

    The guideline says that we should strive for a better design, but still, it’s better than goto; exit approach or doing nothing.

    Ok… but what’s the solution here:

    bool Scanner::scanNodes()
    {
        // code...
        addExtraNodes();
        auto _ = finally([] { removeExtraNodes(); });
    
        // code...
    
        return true;
    }
    

     

    What happened here?

    All I did was wrap the call to removeExtraNodes a particular object that will call a given callable object in its destructor. This is precisely what we need!

    Where can we find that magical finally() code?

    Just see Guideline Support Library/gsl_util.h.

    Under the hood

    The code is short, so I can even paste it here:

     

    template <class F>
    class final_act
    {
    public:
        explicit final_act(F f) noexcept 
          : f_(std::move(f)), invoke_(true) {}
    
        final_act(final_act&& other) noexcept 
         : f_(std::move(other.f_)), 
           invoke_(other.invoke_)
        {
            other.invoke_ = false;
        }
    
        final_act(const final_act&) = delete;
        final_act& operator=(const final_act&) = delete;
    
        ~final_act() noexcept
        {
            if (invoke_) f_();
        }
    
    private:
        F f_;
        bool invoke_;
    };
    

     

    Isn’t that beautiful?!

    The above class takes a callable object – f_ It will call it when it’s about to be destroyed. So even if your code returns early or throws an exception, your cleanup code must be invoked.

    To work nicely with move semantics, there has to be an additional boolean parameter invoke_. This will guarantee that we won’t call the code for temporary objects. See this commit for more information if needed:
    Final_act copy/move semantics is wrong
    .

    In C++17, we have Template argument deduction for class templates – that’s why you can also declare final_act object as:

    final_act _f([] { removeExtraNodes(); })

     

    Before C++17, we had to use the helper function finally to make our life easier:

    template <class F>
    inline final_act<F> finally(const F& f) noexcept
    {
        return final_act<F>(f);
    }
    
    template <class F>
    inline final_act<F> finally(F&& f) noexcept
    {
        return final_act<F>(std::forward<F>(f));
    }
    

     

    So, all in all, we can use finally() the function in the client code. Maybe that could change in C++17 as we’ll get Template argument deduction for class templates.

    What’s nice about this code?

    • Clean, simple code
    • Expressive; no comments needed
    • Does one thing only
    • It’s generic, so it works on anything that’s callable
    • Modern C++: so supports move semantics, noexcept,

    Important note: final act should be noexcept

    As explained many times through the comments in GSL repo (for example, here), other issues and from Final_act can lead to program termination if the final act throws an exception:

    Final_act should be noexcept. It is conceptually just a handy way for the user to conjure up a destructor, and destructors should be noexcept. If something it invokes happens to throw, then the program will terminate.

    In other words, you should write the code that will be called with the same assumptions as other destructor code… so don’t throw anything there. That might be a little limitation when you want to call some ‘normal’ code, not just some clean-up stuff (on the other hand, might that would be a bad design after all?).

    Where could be used?

    Just to be clear: don’t use finally approach too often! With the proper design, your objects shouldn’t work in a global state and take benefit from RAII as much as possible. Still, there are situations where finally it is pleasant to use:

    • transactions. That’s a general term for all actions that should be reverted when something fails. If you copied 95% of a file and got an error, you cannot leave such possibly corrupted files; you have to remove them and maybe start again. If you are connected to a database and want to write some records, you assume it’s atomic.
    • begin/end functions – where you’re required to call end after something starts. As in our example.
    • flag setters. You have a shared flag and set it to a new state, but you have to reset it to the old state when you’re done.
    • resources without RAII support. The guideline shows an example with malloc/free. It might work if you cannot wrap it in an RAII object (for example, by using smart pointers and custom deleters).
    • safely closing the connection – as another example of resource clean-up.

    Do you see other places where final_act can help?

    You can also look at this list: C++ List of ScopeGuard that appeared sometime on Reddit (thread here)

    Summary

    Follow-up post here: link.

    final_act/finally is a beautiful and well-designed tool that can help with the dirty job of cleaning stuff. In your code, you should go for a better approach to clean things/resources, but if that’s not possible final_act is a great solution.

    Do you use similar classes to clean things in your code?

    What’s next?

    if you can’t throw an exception and can’t use it finally, you have a problem. I will solve this issue in my next post.

    Further information

    Recently Bartłomiej Filipek published his first book, C++17 in Detail. If you’d like to learn the new standard effectively and practically, you can check out the book here: https://leanpub.com/cpp17indetail. 

    For Free: Four Vouchers for C++ in Detail

    I’m happy that Bartłomiej Filipek gave me four vouchers for his books. Read here the details to get them: For Free: Four Vouchers to Win

     

     

     

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, moon, Philipp Lenk, Hobsbawm, Charles-Jianye Chen, and Keith Jeffery.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Seminars

    I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.

    Standard Seminars (English/German)

    Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

    • C++ – The Core Language
    • C++ – The Standard Library
    • C++ – Compact
    • C++11 and C++14
    • Concurrency with Modern C++
    • Design Pattern and Architectural Pattern with C++
    • Embedded Programming with Modern C++
    • Generic Programming (Templates) with C++
    • Clean Code with Modern C++
    • C++20

    Online Seminars (German)

    Contact Me

    Modernes C++ Mentoring,

     

     

    0 replies

    Leave a Reply

    Want to join the discussion?
    Feel free to contribute!

    Leave a Reply

    Your email address will not be published. Required fields are marked *