HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

C++ finalizer template

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
finalizertemplatestackoverflow

Problem

Consider, please, my code of the finalizer template:

#include 

template
class finally
{
public:

  finally(F action, const Args&... args) :
      _action(std::bind(action, args...))
  {}
  ~finally()
  {
    if(enabled)
      _action();
  }

  bool enabled = true;

private:

  std::function _action;
};


In particular I would enhance this template to avoid of using a typenames twice. Let's say we need to close some file on leaving some block of code. Then we declare an object:

finally f(close, some_file);


Here I used the standard close function as an action. But I should use int twice in the ``. Is there any way to avoid this?

Solution

You can avoid specifying argument types twice by employing a constructor template:

template
class finally
{
public:
  template
  finally(F action, const Args&... args) { /* business as usual */ }
  ...
};


But that's not everything. The std::bind's arguments are either copied or moved, they are never passed by reference. Thus, despite const Args&... args, you might end up copying your arguments.

To void this issue, consider using perfect forwarding (as well as ditching std::bind):

template
finally(F action, Args&&... args) :
    _action([action, &args...]{ action(std::forward(args)...); }) {}


Another thing worth considering is the exception safety of action. Since you're invoking it in a destructor, you're risking facing std::terminate if it throws. A small try block will fix it. Also, as of now, enabled seems redundant.

And yes, even with all these fixes, the semantics of finally seems rather unpleasant compared to more traditional scope guards. At least to me.

Edit: full code - https://ideone.com/iThn0i

Code Snippets

template<typename F>
class finally
{
public:
  template<typename... Args>
  finally<F>(F action, const Args&... args) { /* business as usual */ }
  ...
};
template<typename... Args>
finally<F>(F action, Args&&... args) :
    _action([action, &args...]{ action(std::forward<Args>(args)...); }) {}

Context

StackExchange Code Review Q#133902, answer score: 4

Revisions (0)

No revisions yet.