5

In test code occasionally I want to set/mock some global variables and at the end of test/scope I want to restore these variables. For example:

BOOST_AUTO_TEST_CASE(HttpClientCsrf)
{
    std::string csrfSave = Http::getCsrfToken();
    ... some test code
    Http::setCsrfToken(csrfSave); // restore csrf
}

Obvious problem here is that if some test code returns (or throws) before reaching the end you won't restore that csrfSave variable. So, simple improvement is to write some simple struct wrapper that restores the value automatically in dtor:

struct CsrfSave
{
    std::string csrfSave;
    CsrfSave()
    {
        csrfSave = Http::getCsrfToken();
    }
    ~CsrfSave()
    {
        Http::setCsrfToken(csrfSave);
    }
};
BOOST_AUTO_TEST_CASE(HttpClientCsrf)
{
    CsrfSave csrfSave;
    ... some test code
    // dtor of CsrfSave restores csrf
}

This solves the problem in general, however, for each function you'd need to write lots of boilerplate code. So, the question is: what are shortest and simplest approaches could be used to achieve the same, while minimizing and possibly avoiding all that boilerplate.

One such approach that I use, and I'm not too happy about it:

BOOST_AUTO_TEST_CASE(HttpClientCsrf)
{
    std::string csrfSave = RBX::Http::getLastCsrfToken();
    shared_ptr<void> csrfSaveScope(NULL, [&](void*) {
        RBX::Http::setLastCsrfToken(csrfSave);
    });
    ... some test code
}

Is there anything better? I'd prefer to avoid writing any utility classes and would prefer to avoid boost (unless it's something that's included in next std). This kind of pattern happens quite often in different projects (that do not share code) and each time I end up writing either simple struct wrapper or generic wrapper that takes lambda and I would like to see other approaches that can be coded in-place just like I showed in my shared_ptr exmpale

Pavel P
  • 15,789
  • 11
  • 79
  • 128
  • You can make a generic scope guard yourself and pass a function to it's constructor. Something like [this question](https://stackoverflow.com/questions/22656542/call-function-when-leaving-scope). – François Andrieux May 04 '18 at 19:59
  • 2
    Sure - unique_ptr. Why do you need shared_ptr? unique_ptr with custom deleter should do the trick – Severin Pappadeux May 04 '18 at 20:21
  • @SeverinPappadeux isn't unique_ptr uses deleter as a template param? would it work the same seamlessly? – Pavel P May 05 '18 at 17:36
  • Yes, you need template parameter. Code `auto main() -> int { int ttt = 77; auto deleter = [&](void*) { std::cout << "Deleter called\n"; std::cout << ttt << "\n"; // do something with capture here instead of print }; std::unique_ptr p((void*)1, deleter); std::cout << "done\n"; return 0; }` – Severin Pappadeux May 05 '18 at 17:51
  • @SeverinPappadeux that's clearly too messy compared to my shared_ptr sample – Pavel P May 05 '18 at 18:47
  • @FrançoisAndrieux Yes I can make everything, but "I'd prefer to avoid writing any utility classes" – Pavel P May 05 '18 at 18:48
  • @SeverinPappadeux and what all that `auto main() -> int`? – Pavel P May 05 '18 at 18:53
  • wrt `auto main() -> int` http://levelofindirection.com/blog/east-end-functions.html – Severin Pappadeux May 05 '18 at 19:10
  • As author states "not sure the consistency argument is as strong here" - and I totally agree. That's just pointless and confusing to use in simple code. Especially for `int main()` `auto main() -> int` just has totally opposite effect and completely breaks consistency. I could understand that use in function declarations but for freestanding functions?! c'mon, that's just messy – Pavel P May 05 '18 at 19:23
  • well, I rather like it. Actually, for my code I use `func`, so it would be `func main() -> int` – Severin Pappadeux May 05 '18 at 23:57
  • `that's clearly too messy compared to my shared_ptr sample` well, that is why we have preprocessor. I could collapse a lot of boilerplate around this code into single line – Severin Pappadeux May 05 '18 at 23:58

2 Answers2

9

(Adding these comments in later. Apologies for just pasting my code. I'm doing 3 things at once here at work )

Obviously you want some object with a destructor that gets cleaned up at function scope.

A deferred call is a template that holds the lambda you specify. Since it can only take the given lambda by move, you have ownership semantics. The only thing left is to return one of these as an rvalue reference. That's the job of the defer() function. It takes in your lambda and returns it in a deferred call. You save that object in a local variable and then C++ takes care of the rest.

This was actually the thing that really helped me "get" move semantics in the first place. I admit this technique is not original

auto cleanup = defer([] { /* call me at end of scope */ );

Note that this technique is not original.

// =============================================================================
// deferred_call:
// --------------
// This struct enables us to implement deferred function calls simply in
// the defer() function below.  It forces a given function to automatically
// be called at the end of scope using move-only semantics.  Most
// commonly, the given function will be a lambda but that is not required.
// See the defer() function (below) for more on this
// =============================================================================
template <typename FUNC>
struct deferred_call
{
    // Disallow assignment and copy


    deferred_call(const deferred_call& that) = delete;
    deferred_call& operator=(const deferred_call& that) = delete;

    // Pass in a lambda

    deferred_call(FUNC&& f) 
        : m_func(std::forward<FUNC>(f)), m_bOwner(true) 
    {
    }

    // Move constructor, since we disallow the copy

    deferred_call(deferred_call&& that)
        : m_func(std::move(that.m_func)), m_bOwner(that.m_bOwner)
    {
        that.m_bOwner = false;
    }

    // Destructor forces deferred call to be executed

    ~deferred_call()
    {
        execute();
    }

    // Prevent the deferred call from ever being invoked

    bool cancel()
    {
        bool bWasOwner = m_bOwner;
        m_bOwner = false;
        return bWasOwner;
    }

    // Cause the deferred call to be invoked NOW

    bool execute()
    {
        const auto bWasOwner = m_bOwner;

        if (m_bOwner)
        {
            m_bOwner = false;
            m_func();
        }

        return bWasOwner;
    }

private:
    FUNC m_func;
    bool m_bOwner;
};


// -----------------------------------------------------------------------------
// defer:  Generic, deferred function calls
// ----------------------------------------
//      This function template the user the ability to easily set up any 
//      arbitrary  function to be called *automatically* at the end of 
//      the current scope, even if return is called or an exception is 
//      thrown.  This is sort of a fire-and-forget.  Saves you from having
//      to repeat the same code over and over or from having to add
//      exception blocks just to be sure that the given function is called.
//
//      If you wish, you may cancel the deferred call as well as force it
//      to be executed BEFORE the end of scope.
//
// Example:
//      void Foo()
//      {
//          auto callOnException  = defer([]{ SomeGlobalFunction(); });
//          auto callNoMatterWhat = defer([pObj](pObj->SomeMemberFunction(); });
//
//          // Do dangerous stuff that might throw an exception ...
//
//          ...
//          ... blah blah blah
//          ...
//
//          // Done with dangerous code.  We can now...
//          //      a) cancel either of the above calls (i.e. call cancel()) OR
//          //      b) force them to be executed (i.e. call execute()) OR
//          //      c) do nothing and they'll be executed at end of scope.
//
//          callOnException.cancel();    // no exception, prevent this from happening
//
//          // End of scope,  If we had not canceled or executed the two
//          // above objects, they'd both be executed now.
//      }
// -----------------------------------------------------------------------------

template <typename F>
deferred_call<F> defer(F&& f)
{
    return deferred_call<F>(std::forward<F>(f));
}
Joe
  • 5,394
  • 3
  • 23
  • 54
  • @Joe it's a good generic solution, but I'm trying to come up with something that works without writing any code similarly to my last example. – Pavel P May 04 '18 at 21:28
  • 1
    @Pavel This does work without writing any code. Treat the code in this answer like a library, and your last example would instead be: `auto restoreCsrf = defer([&] { RBX::Http::setLastCsrfToken(csrfSave); });` – Justin May 04 '18 at 22:18
  • @Pavel, it is as Justin says. Once you have this, it takes only one line of code to achieve what you need. – Joe May 04 '18 at 23:03
  • I know exactly what it is and how it works. My hacky solution works pretty much the same way without writing any utility code and I mainly look for anything better while "I'd prefer to avoid writing any utility classes". – Pavel P May 05 '18 at 17:33
  • When I replied, you had not written that sentence. You added it in later after I had replied. Please don't quote it as if I ignored it – Joe May 07 '18 at 03:18
3

The obvious solution is to create an object that does the cleanup in its destructor that you then create on the stack at the appropriate point. A function that takes a std::function as argument and returns an object that then runs that function in its destructor, would probably be my prefered implementation. So that I can do something like

const auto guard{makeScopeGuard(...)};

and then just pass an appropriate lambda as "..." and know that whenever guard is destroyed - due to going out of scope or due to an exception or whatever, then my lambda runs and does whatever cleanup is needed.

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70