88#define MAMBA_UTIL_SYNCHRONIZED_VALUE_HPP
99
1010#include < concepts>
11+ #include < functional>
1112#include < mutex>
1213#include < shared_mutex>
1314#include < tuple>
15+ #include < utility>
1416
1517namespace mamba ::util
1618{
@@ -35,6 +37,26 @@ namespace mamba::util
3537 template <class T , template <class ...> class U >
3638 constexpr bool is_type_instance_of_v = is_type_instance_of<T, U>::value;
3739
40+ // / `true` if the instances of two provided types can be compared with operator==.
41+ // / Notice that this concept is less restrictive than `std::equality_comparable_with`,
42+ // / which requires the existence of a common reference type for T and U. This additional
43+ // / restriction makes it impossible to use it in the context here (originally of sparrow), where
44+ // / we want to compare objects that are logically similar while being "physically" different.
45+ // Source:
46+ // https://github.com/man-group/sparrow/blob/66f70418cf1b00cc294c99bbbe04b5b4d2f83c98/include/sparrow/utils/mp_utils.hpp#L604-L619
47+
48+ template <class T , class U >
49+ concept weakly_equality_comparable_with = requires (
50+ const std::remove_reference_t <T>& t,
51+ const std::remove_reference_t <U>& u
52+ ) {
53+ { t == u } -> std::convertible_to<bool >;
54+ { t != u } -> std::convertible_to<bool >;
55+ { u == t } -> std::convertible_to<bool >;
56+ { u != t } -> std::convertible_to<bool >;
57+ };
58+
59+
3860 // ///////////////////////////
3961
4062
@@ -256,7 +278,9 @@ namespace mamba::util
256278
257279 // / Constructs with a provided value as initializer for the stored object.
258280 template <typename V>
259- requires std::assignable_from<T&, V> and (not std::same_as<this_type, std::decay_t <V>>)
281+ requires (not std::same_as<T, std::decay_t <V>>)
282+ and (not std::same_as<this_type, std::decay_t <V>>)
283+ and std::assignable_from<T&, V>
260284 synchronized_value (V&& value) noexcept
261285 : m_value(std::forward<V>(value))
262286 {
@@ -266,6 +290,11 @@ namespace mamba::util
266290 // the definition here.
267291 }
268292
293+ // / Constructs with a provided value as initializer for the stored object.
294+ // NOTE: this is redundant with the generic impl, but required to workaround
295+ // apple-clang failing to properly constrain the generic impl.
296+ synchronized_value (T value) noexcept ;
297+
269298 // / Constructs with a provided initializer list used to initialize the stored object.
270299 template <typename V>
271300 requires std::constructible_from<T, std::initializer_list<V>>
@@ -284,14 +313,17 @@ namespace mamba::util
284313 the call. If `SharedMutex<M> == true`, the lock is a shared-lock for the provided
285314 `synchronized_value`'s mutex.
286315 */
287- template <std::equality_comparable_with<T> U, Mutex OtherMutex>
316+ template <std::default_initializable U, Mutex OtherMutex>
317+ requires std::assignable_from<T&, U>
288318 auto operator =(const synchronized_value<U, OtherMutex>& other) -> synchronized_value&;
289319
290320 /* * Locks and assign the provided value to the stored object.
291321 The lock is released before the end of the call.
292322 */
293323 template <typename V>
294- requires std::assignable_from<T&, V> and (not std::same_as<this_type, std::decay_t <V>>)
324+ requires (not std::same_as<T, std::decay_t <V>>)
325+ and (not std::same_as<this_type, std::decay_t <V>>)
326+ and std::assignable_from<T&, V>
295327 auto operator =(V&& value) noexcept -> synchronized_value&
296328 {
297329 // NOTE: when moving the definition outside the class,
@@ -303,6 +335,13 @@ namespace mamba::util
303335 return *this ;
304336 }
305337
338+ /* * Locks and assign the provided value to the stored object.
339+ The lock is released before the end of the call.
340+ */
341+ // NOTE: this is redundant with the generic impl, but required to workaround
342+ // apple-clang failing to properly constrain the generic impl.
343+ auto operator =(const T& value) noexcept -> synchronized_value&;
344+
306345 /* * Locks and return the value of the current object.
307346 The lock is released before the end of the call.
308347 If `SharedMutex<M> == true`, the lock is a shared-lock.
@@ -465,12 +504,12 @@ namespace mamba::util
465504 /* * Locks (shared if possible) and compare equality of the stored object's value with the
466505 provided value.
467506 */
468- auto operator ==(const std::equality_comparable_with <T> auto & other_value) const -> bool ;
507+ auto operator ==(const weakly_equality_comparable_with <T> auto & other_value) const -> bool ;
469508
470509 /* * Locks both (shared if possible) and compare equality of the stored object's value with
471510 the provided value.
472511 */
473- template <std::equality_comparable_with <T> U, Mutex OtherMutex>
512+ template <weakly_equality_comparable_with <T> U, Mutex OtherMutex>
474513 auto operator ==(const synchronized_value<U, OtherMutex>& other_value) const -> bool ;
475514
476515 auto swap (synchronized_value& other) -> void;
@@ -508,6 +547,12 @@ namespace mamba::util
508547 template <std::default_initializable T, Mutex M>
509548 synchronized_value<T, M>::synchronized_value() noexcept (std::is_nothrow_default_constructible_v<T>) = default ;
510549
550+ template <std::default_initializable T, Mutex M>
551+ synchronized_value<T, M>::synchronized_value(T value) noexcept
552+ : m_value(std::move(value))
553+ {
554+ }
555+
511556 template <std::default_initializable T, Mutex M>
512557 synchronized_value<T, M>::synchronized_value(const synchronized_value& other)
513558 {
@@ -516,7 +561,8 @@ namespace mamba::util
516561 }
517562
518563 template <std::default_initializable T, Mutex M>
519- template <std::equality_comparable_with<T> U, Mutex OtherMutex>
564+ template <std::default_initializable U, Mutex OtherMutex>
565+ requires std::assignable_from<T&, U>
520566 auto synchronized_value<T, M>::operator =(const synchronized_value<U, OtherMutex>& other)
521567 -> synchronized_value<T, M>&
522568 {
@@ -526,6 +572,14 @@ namespace mamba::util
526572 return *this ;
527573 }
528574
575+ template <std::default_initializable T, Mutex M>
576+ auto synchronized_value<T, M>::operator =(const T& value) noexcept -> synchronized_value&
577+ {
578+ auto _ = lock_as_exclusive (m_mutex);
579+ m_value = value;
580+ return *this ;
581+ }
582+
529583 template <std::default_initializable T, Mutex M>
530584 template <typename V>
531585 requires std::constructible_from<T, std::initializer_list<V>>
@@ -584,15 +638,16 @@ namespace mamba::util
584638 }
585639
586640 template <std::default_initializable T, Mutex M>
587- auto synchronized_value<T, M>::operator ==(const std::equality_comparable_with<T> auto & other_value
641+ auto
642+ synchronized_value<T, M>::operator ==(const weakly_equality_comparable_with<T> auto & other_value
588643 ) const -> bool
589644 {
590645 auto _ = lock_as_readonly (m_mutex);
591646 return m_value == other_value;
592647 }
593648
594649 template <std::default_initializable T, Mutex M>
595- template <std::equality_comparable_with <T> U, Mutex OtherMutex>
650+ template <weakly_equality_comparable_with <T> U, Mutex OtherMutex>
596651 auto
597652 synchronized_value<T, M>::operator ==(const synchronized_value<U, OtherMutex>& other_value) const
598653 -> bool
@@ -623,7 +678,6 @@ namespace mamba::util
623678 {
624679 return std::make_tuple (std::forward<SynchronizedValues>(sync_values).synchronize ()...);
625680 }
626-
627681}
628682
629683#endif
0 commit comments