Current best practice for using a lock_guard looks like this:
// introduce scope to take the lock
{
lock_guard lock(sync); // sync is an accessible mutex object
do_protected_stuff(); // may throw, but the mutex is released regardless
}
// no exception, mutex is released in normal flow
I don't like the scope being introduce without a statement. I think it detracts from readability, and I think it's ugly. It occurred to me that what I wanted was the equivalent of modern Python's with statement:
with Lock() as lock: # corresponds to lock+mutex in C++
do_protected_stuff()
I thought about it for a while and designed a syntax hack that's gotten me partway there:
#include <iostream>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using std::cout;
using std::endl;
//=============================================
// This is the "novel" part
// See main() for usage
//=============================================
// a utility class
class with_lock_
{
public:
with_lock_(boost::mutex &mtx_) : lock(mtx_), new_lock(true) {}
operator bool() { bool ret = new_lock; new_lock = false; return ret; }
private:
boost::lock_guard<boost::mutex> lock;
bool new_lock;
};
// a macro to hide the gory details
#define with_lock_guard(SYNC) for (with_lock_ lk_(SYNC); lk_; )
//==============================================
// some global stuff for multi-thread action
// a mutex
boost::mutex sync;
// a value protected by the mutex
float zz = 0.0f;
// a thread function that will change the value and crow about it
void get_it_yourself()
{
boost::lock_guard<boost::mutex> lock(sync);
zz = 99999.99999f;
cout << endl << " --- MINE!!" << endl;
}
//==============================================
int main()
{
boost::thread runit;
//=============================================
// This is the intended use...
// a little syntactic hack, inspired by Python
//=============================================
with_lock_guard (sync)
{
runit = boost::thread(get_it_yourself);
cout << "Starting... ";
boost::this_thread::sleep(boost::posix_time::millisec(500));
cout << "OK! Z = " << zz << endl;
}
// wait for that thread to complete
runit.join();
return 0;
}
Accept, for this discussion, that having a keyword to introduce the scope is preferable to introducing scope with just a brace. This hack lets you pretend you have a real with-like statement: you can follow it with a single or compound statement.
I do have a concern regarding the scope of the with_lock_ object. As I read things, at some level (persisting even into C++11?) the scope of a variable introduced within a for statement is the block enclosing the for. In practice, it's working OK for me with MSVS 2008; there is a compiler option regarding this, which defaults to the behavior I need in this situation.
Even without backing off from the basic idea, I see room for improvement. For one thing, a true with statement in C++ would be able to accept other types of RAII-based objects, as Python does. But so far in practice, I've only really the undesirable syntax with lock_guard.
What I'm looking for is a way to generalize this feature such that, at least, any mutex type, from any namespace, can be used. Better, if any lock type can be used. A cleaner implementation than abusing for or the operator bool() would be of interest.
Also: I'm not concerned if it breaks older compilers, but it would good to know if this works or doesn't in platforms/compilers other than Windows/MSVS2008+. Comments decrying the whole abomination are fine.
for looppage describing how the compiler determines the scope according to the flags it's given (default is the C++ standard: the variable is scoped to the loop body). This is for MSVC'03; the newer versions appear to have the same options available. – user1201210 Oct 8 '12 at 19:35