-1

I need to protect data container with std::mutex that is used in for loop like this:

for (auto it = data.begin(); it != data.end(); ++it)
{
    std::cout << it->param;
    ...
}

I can think of several options but they are ugly like this:

{ // artificial scope
    std::scoped_lock lock(myMutex)
    for (auto it = data.begin(); it != data.end(); ++it)
    {
        std::cout << it->param;
        ...
    }
}

Is there a nice-looking method to achieve this? I am thinking about something like the following (C++17), but this does not compile. :(

for (std::scoped_lock lock(myMutex), auto it = data.begin(); it != data.end(); ++it)

Or,

for (auto [lock, it] = std::pair(std::scoped_lock lock(myMutex), data.begin()); it != data.end(); ++it)
Sergey
  • 31
  • 2
  • 3
    Artificial scope doesn't have to be artificial - maybe you can make it a function? – Yksisarvinen Nov 28 '19 at 12:28
  • 7
    Um, how exactly is the structured binding example less "ugly" than the use of explicit scope? Because it lacks a curly brace? The explicit scope makes a clear distinction between the loop itself and the mutex you're building around the loop. Not to mention, the scoped loop only contains code relevant to the loop itself, rather than having a giant `scoped_lock` in the middle of your loop logic. Basically, by what standard are we to decide what is "a nice-looking method"? By my standards, the scoped version is the best of the lot. – Nicol Bolas Nov 28 '19 at 12:33
  • 2
    "Beauty is in the eye of the beholder." Which makes this pretty subjective. – Some programmer dude Nov 28 '19 at 12:34
  • 1
    what you call "artificial" is completely natural when you consider that RAII is an important concept which should be familiar, and `std::scoped_lock` is nothing but a RAII type. I wouldnt call it ugly, just the opposite, such code shows the beauty of C++ – 463035818_is_not_an_ai Nov 28 '19 at 12:38
  • OK, maybe “ugly” is rather strong word. Let’s call it “suboptimal” because it needs 3 extra lines of code and personally I do not like artificial scopes. I really like how c++20 variant (see below) looks like, but it is not yet widely supported though. – Sergey Nov 28 '19 at 12:49

2 Answers2

3

introduced range-based for loops with initializer:

for (std::scoped_lock lock(myMutex); auto &&x : data)
{
    std::cout << x.param;
}

This feature is already supported by GCC 9 and Clang 8, but requires the -std=c++2a flag.

metalfox
  • 6,301
  • 1
  • 21
  • 43
0

I wouldn't overlook the obvious solution of extracting the "ugly" block into a separate method, as suggested in the comments. But you can also just do

std::unique_lock<std::mutex> guard(mutex);
for (auto it = data.begin(); it != data.end(); ++it) {
    // ...
}
guard.unlock();

Prefer std::lock_guard over std::unique_lock if you do not intend on releasing the lock yourself; scoped_lock is meant to lock several mutexes as well.

elbrunovsky
  • 442
  • 5
  • 11