Search code examples
c++multithreadingc++11boostreference-wrapper

Using reference_wrapper as condition_variable predicate


Note: The following applies equally to Boost.Thread and C++11 threads.

I have a condition variable which condition is actually a simple boolean variable.

// assume these are global
mutex m;
condition_variable c;
boolean b = false;

I wanted to use the wait(lock, predicate) syntax. I could use for example a lambda:

c.wait(lock, [] () { return b; });

But I supposed there should be a more idiomatic way to wrap a variable as a callable. So I found out that reference_wrapper provides an operator() that retrieves the wrapped value. Therefore I tried:

c.wait(lock, cref(b));

But g++ (4.9.1) does not compile it, arguing no matching function for call to '(boost::reference_wrapper<const bool>) ()' (if I use std::ref the error is somewhat different, but still not compiling).

Shouldn't a reference_wrapper qualify as a proper predicate for a condition variable? If not, why? And what would be the right wrapper for b in this case?

EDIT: So @Praetorian has explained quite right my mistake, but, is there really nothing like this on Boost or the standard (there may be mistakes here):

template<typename T>
struct as_callable {
    T &objref;
    as_callable(T &r) : objref(r) {}
    T &operator()() {
        return objref;
    }
};

Solution

  • std::reference_wrapper::operator() is only available when the reference_wrapper is storing a callable, which a plain bool isn't. Use a lamdba for the predicate.

    Also consider using std::atomic_bool instead of a plain bool if that boolean is being modified in a different thread from the one where your condition variable is waiting on it.


    If you really want to use a wrapper instead of a lambda you could write a trivial wrapper class that overloads operator() and returns the stored boolean value when invoked.

    struct bool_wrapper
    {
        bool_wrapper(bool& b) : b_(&b) {}
        bool *b_;
    
        bool operator()() const noexcept { return *b_; }
    };
    

    Now you can use this class to wrap the boolean and pass it to condition_variable::wait as

    c.wait(lock, bool_wrapper(b));
    

    Live demo