Kai Walz
Kai Walz

Reputation: 796

how to create guard similar to boost::lock_guard

I want to create a guard, which locks a function on construction and unlocks it on destruction, e.g. calling the function with false and true.

class A {
void enable( bool flag );
};

within another method, I want to use:

A::anotherMethod( ... ) {
  block_guard(A::enable); // now A::enable(false)
  // some operation
} // now A::enable(true)

my ideas:

using template

template < class T >
class block_guard {
  T t_;
public:
  block_guard( T& t ) : t_(t) {
    t_(false);
  }
  ~block_guard() {
    t_(true);
  }
};

the question is, how to instantiate the template? maybe with boost::bind?

using boost::function

class block_guard {
  typedef boost::function< void (bool) > T;
  T t_;
public:
  block_guard( T& t ) : t_(t) {
    t_(false);
  }
  ~block_guard() {
    t_(true);
  }
};

this works fine, but the call seems to be very complicated with

block_guard bg(boost::function< void (bool) >(boost::bind(&A::enable, pointer-to-A, _1));

any ideas? maybe there is another, much simpler way?

Upvotes: 1

Views: 269

Answers (2)

Sebastian Redl
Sebastian Redl

Reputation: 72063

First, realize that the member function is not all you need; you also need the object to invoke it on. There is no way in C++ for an object created in a function to implicitly capture the current this pointer.

I'm going to assume you don't have C++11 available. If you do, using your second solution with a lambda expression is easiest.

Now, if you don't care about the slight performance hit of boost::function (and you shouldn't), the second solution is good, but I would modify it slightly to make it more convenient to use by pulling the bind into the constructor.

class block_guard {
  typedef boost::function< void (bool) > block_fn;
  block_fn block_fn_;
public:
  // For non-member functions and function objects:
  template <typename Fn>
  block_guard(Fn fn) : block_fn_(fn) {
    block_fn_(false);
  }
  // For member functions:
  template <typename T, typename Ret>
  block_guard(T* obj, Ret (T::*fn)(bool)) : block_fn_(boost::bind(fn, obj, _1)) {
    block_fn_(false);
  }
  ~block_guard() {
    block_fn_(true);
  }
};

Usage:

block_guard guard(this, &A::enable);

I use a Ret parameter here because there's no reason not to allow functions that return something - the return value will simply get ignored.

If you don't want boost::function, the thing will get less easy to use, because you have to template the block guard. It becomes useful to make a block_guard specifically for member functions then. You also lose the ability to use non-void functions.

template <typename T>
class block_guard {
  typedef void (T::*block_fn)(bool);
  T* obj_;
  block_fn block_fn_;
public:
  block_guard(T* obj, block_fn fn) : obj_(obj), block_fn_(fn) {
    (obj_->*block_fn_)(false);
  }
  ~block_guard() {
    (obj_->*block_fn_)(true);
  }
};

Usage:

block_guard<A> guard(this, &A::enable);

Upvotes: 2

Balog Pal
Balog Pal

Reputation: 17193

Yes, there is a much simpler way, forget templates, generic thing and whatever not necessary and focus on the task.

All you need is a class with a ctor and a dtor. Write the dtor first, it reveals what you will need to work. Then write the ctor, taking arguments as needed. Lastly make the unwanted functions deleted (cctor, op=). Done.

Not generic, but straight to the point.

Upvotes: 0

Related Questions