3
\$\begingroup\$

Consider, please, my code of the finalizer template:

#include <functional>


template<typename F, typename... Args>
class finally
{
public:

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

  bool enabled = true;

private:

  std::function<void()> _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<int(int), int> f(close, some_file);

Here I used the standard close function as an action. But I should use int twice in the <int(**int**), **int**>. Is there any way to avoid this?

\$\endgroup\$
4
\$\begingroup\$

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

template<typename F>
class finally
{
public:
  template<typename... Args>
  finally<F>(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<typename... Args>
finally<F>(F action, Args&&... args) :
    _action([action, &args...]{ action(std::forward<Args>(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

\$\endgroup\$
  • \$\begingroup\$ The enabled is needed to manually switch off the finalizer. \$\endgroup\$ – Serge Roussak Jul 12 '16 at 15:49
0
\$\begingroup\$

Have you considered something like,

typedef std::unique_ptr<FILE, int(*)(FILE*)> FileHandle;
FileHandle f {fopen("sample.txt"), fclose};

Of course, you need a typedef for every custom deleter you want to have. If you're looking for a generic finalizer then, you basically got what you wanted with a price of std::function on it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.