3

I am designing a function in C++20 that needs to be callable on both containers and views. However, I want to ensure thread safety and avoid concurrency issues when the function is called in parallel on the same view / container. The function does not perform any modifications and simply iterates over the view / container.

One option is to use std::ranges::range auto &&, as the parameter type for the function, allowing both containers and views to be passed. However, this raises concerns about potential concurrency issues if the function operates on a view, since iterating over a view in parallel is not necessarily thread safe.

Another option is to use std::ranges::view auto, as the parameter type, which guarantees thread safety since the view gets copied. However, this restricts the function's usage to only views and not containers directly.

What would be the recommended approach to design the function interface in this scenario?

Eugene
  • 6,194
  • 1
  • 20
  • 31
  • 1
    I mean, is *copying* a `view` while another thread is manipulating the same object guaranteed to be thread safe? I wouldn't think so. I feel like you at least need one of the caller (by handling most of the thread safety itself) or the view/container type (by ensuring some amount of atomicity) to cooperate with you for any of this to make sense. – HTNW Jul 03 '23 at 22:40
  • "*The function does not perform any modifications and simply iterates over the view / container.*" The parameters can be constrained to be `ranges::constant_range`, perhaps. – 康桓瑋 Jul 04 '23 at 01:40
  • `ranges::constant_range` is C++23. The question is C++20. – Eugene Jul 05 '23 at 01:01

1 Answers1

3

On the one hand, I would say that the thread safety of the views passed to your algorithm is outside of your control. This seems like an issue that should be addressed by the caller.

But if you want to do this, you could create two overloads; one taking a reference to a range and the other taking a copy of a view.

namespace details {
void algorithm_impl(std::ranges::range auto&& range) {
    // Implement the actual algorithm
}
}

// Range overload
template<std::ranges::range Range>
requires(!std::ranges::view<Range>)
void algorithm(Range&& range) {
    details::algorithm_impl(range);
}

// View overload
void algorithm(std::ranges::view auto view) {
    details::algorithm_impl(range);
}
Wutz
  • 2,246
  • 13
  • 15