元ネタ:
http://cpplover.blogspot.com/2011/06/multiplelockguard.html
いちいち multiple_lock_guard< std::mutex, std::mutex, std::mutex >
とか書くのは面倒なので,
関数にして auto
で束縛できるようにしてみました.
なお,テスト等は全く行っていないので,使う場合は自己責任でお願いします.
#include <tuple> #include <memory> #include <mutex> template< class Mutex > struct unlock { void operator()( Mutex* m ) const { m->unlock(); } }; // クラスではなく関数にする(型推論しやすいよう) template< class... Args, class Result = std::tuple<std::unique_ptr<Args, unlock<Args>>...> > Result make_lock_guard( Args&... args ) { std::lock( args... ); return Result( std::addressof(args)... ); }
専用のクラスを作るのではなく, unique_ptr
と tuple
を組み合わせています.
これなら copy 禁止も move 対応もしなくていいので,かなり楽です.
std::mutex m1, m2, m3 ; int main() { auto lock = make_lock_guard( m1, m2, m3 ) ; }
このように使います.
追記
std::unique_lock
の adopt_lock
を使うという手もありました.
unique_ptr
を使ったときに比べて空間効率は悪化しますが,標準ですし,こちらを使うのも良いと思います.
#include <tuple> #include <memory> #include <mutex> // 補助関数. 受け取った mutex を adopt_lock する template< class Mutex > std::unique_lock<Mutex> adopt_unique_lock( Mutex& m ) { return std::unique_lock<Mutex>( m, std::adopt_lock ); } // クラスではなく関数にする(型推論しやすいよう) template< class... Args, class Result = std::tuple<std::unique_lock<Args>...> > Result make_lock_guard( Args&... args ) { std::lock( args... ); return Result( adopt_unique_lock(args)... ); } std::mutex m1, m2, m3; int main() { auto lock = make_lock_guard( m1, m2, m3 ); }
// make_lock_guard
って名前はイマイチだなぁ….
さらに追記
http://d.hatena.ne.jp/Cryolite/20110721#p1
defer_lock は知らなかった. これは便利ですね.
template<class Mutex> inline std::unique_lock<Mutex> make_deferred_lock( Mutex& m ) { return std::unique_lock<Mutex>( m, std::defer_lock ); }
といったヘルパ関数を用意して,
std::mutex m1, m2, m3; void f() { auto lk1 = make_deferred_lock(m1); auto lk2 = make_deferred_lock(m2); auto lk3 = make_deferred_lock(m3); std::lock( lk1, lk2, lk3 ); // 処理 }
という感じで処理すれば, std::unique_lock
の変数にアクセスしやすいので,
変に std::tuple
に詰め込むより,ずっとコーディングが楽になりそうです.
とはいえ, std::unique_lock
は 若干オーバースペックな気もしないでもないので,
std::unique_ptr
を使った方法も,悪くはないんじゃないかなー,とか.
もいっちょ追記
std::unique_lock
を製作するヘルパ関数を幾つか書いたけど,よく考えたら,
template< class Mutex, class... Args > inline std::unique_lock<Mutex> make_unique_lock( Mutex& m, Args&&... args ) { return std::unique_lock<Mutex>( m, std::forward<Args>(args)... ); }
こういうヘルパ関数が一つ有れば,それで十分なことに気づきました.
std::mutex m1, m2; void f() { // 単純にロックする auto lk = make_unique_lock( m1 ); // 処理... } void g() { // try lock auto lk = make_unique_lock( m1, std::try_lock ); if( lk ) { // あるいは if( auto lk = make_unique_lock(...) ) としてもよい // 処理... } } void h() { // adopt lock std::lock( m1, m2 ); auto lk1 = make_unique_lock( m1, std::adopt_lock ); auto lk2 = make_unique_lock( m2, std::adopt_lock ); // 処理... } void i() { // defer lock auto lk1 = make_unique_lock( m1, std::defer_lock ); auto lk2 = make_unique_lock( m2, std::defer_lock ); std::lock( lk1, lk2 ); // 処理... }
なので,これからは,これを使おうかと.
// っていうか,これは標準ライブラリで用意するべき関数な気がが