I have the following array of mutexes:
std::mutex mtx[5];
And I would like to lock them all with an RAII style:
std::lock_guard<std::mutex> grd[5] { mtx[0], mtx[1], mtx[2], mtx[3], mtx[4] };
While the above code works, it isn't ideal as I can't write it independently of the size of the array (here 5).
Is there a way to do that? Shall I work with template magic to create an std::initializer_list
out of an array? (is that possible?)
I'm open to using std::array
or std::vector
instead of C-style arrays, I used those here for conciseness. Ideally this works in C++14 but any solution up to latest standards is fine.
What you want is std::scoped_lock
. It takes N mutexes and locks them on creation and unlocks on destruction. That would give you
std::scoped_lock sl{mtx[0], mtx[1], mtx[2], mtx[3], mtx[4]};
If that's still too verbose you can wrap that in a factory function like
// function that actually creates the lock
template<typename Mutexes, std::size_t N, std::size_t... Is>
auto make_scoped_lock(Mutexes (&mutexes)[N], std::index_sequence<Is...>)
{
return std::scoped_lock{mutexes[Is]...};
}
// helper function so you don't have to create your own index_sequence
template<typename Mutexes, std::size_t N>
auto make_scoped_lock(Mutexes (&mutexes)[N])
{
return make_scoped_lock(mutexes, std::make_index_sequence<N>{});
}
int main()
{
std::mutex mtx[5];
auto lock = make_scoped_lock(mtx);
}
If you switch to using a std::array
to hold the mutexes then the code can be simplified to a call to std::apply
to do the expansion of the array into a parameter pack like
template<typename Mutexes>
auto make_scoped_lock(Mutexes& mutexes)
{
return std::apply([](auto&... mutexes) { return std::scoped_lock{mutexes...}; },
mutexes);
}
int main()
{
std::array<std::mutex, 5> mtx;
auto sl = make_scoped_lock(mtx);
}