Last active
March 6, 2025 23:43
-
-
Save saxbophone/d7e71ea0bd60ea4443c8ad7bd05e1a92 to your computer and use it in GitHub Desktop.
Reloaded version of my Seizable™ concept
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <mutex> // locks | |
#include <shared_mutex> // shared_mutex | |
#include <utility> // exposing structured-binding support for SynchroniseAll | |
template <typename T> | |
class Synchronised { | |
public: | |
using Mutex = std::shared_mutex; | |
using ReadOnlyLock = std::shared_lock<Mutex>; | |
using ReadWriteLock = std::unique_lock<Mutex>; | |
class Watched { | |
public: | |
constexpr const T& at() const { return *_watched; } | |
constexpr const T& operator*() const { return at(); } | |
constexpr const T* operator->() const { return _watched; } | |
private: | |
constexpr Watched(const T& data, Mutex& mutex) | |
: _watched{&data} | |
, _lock{mutex} | |
{} | |
const T* _watched; | |
ReadOnlyLock _lock; | |
friend Synchronised; | |
}; | |
class Seized { | |
public: | |
constexpr const T& at() const { return *_seized; } | |
constexpr T& at() { return *_seized; } | |
constexpr const T& operator*() const { return at(); } | |
constexpr T& operator*() { return at(); } | |
constexpr const T* operator->() const { return _seized; } | |
constexpr T* operator->() { return _seized; } | |
private: | |
constexpr Seized(T& data, Mutex& mutex) | |
: _seized{&data} | |
, _lock{mutex} | |
{} | |
T* _seized; | |
ReadWriteLock _lock; | |
friend Synchronised; | |
}; | |
constexpr Synchronised() = default; | |
constexpr Synchronised(const T& value) : _data{value} {} | |
constexpr T operator=(const T& value) { return set(value); } | |
constexpr T set(const T& value) { | |
ReadWriteLock write_lock{_mutex}; | |
_data = value; | |
return _data; | |
} | |
constexpr operator T() const { return get(); } | |
constexpr T get() const { | |
ReadOnlyLock read_lock{_mutex}; | |
return _data; | |
} | |
// NOTE: Since Synchronised<> provides atomic operations only, we don't | |
// expose any operations that return pointer or reference. | |
// Use .watch() or .seize() for that. | |
constexpr Watched watch() { return {_data, _mutex}; } | |
constexpr Seized seize() { return {_data, _mutex}; } | |
private: | |
T _data; | |
mutable Mutex _mutex; | |
template <typename... Ts> // TODO: refactor if possible to avoid this friend declaration | |
friend class SynchroniseAll; | |
}; | |
template <typename... Ts> | |
class SynchroniseAll { | |
public: | |
constexpr SynchroniseAll(Synchronised<Ts>&... synchroniseds) | |
: _data{synchroniseds._data...} // NOTE: Relies upon friendship in order to access private members | |
, _lock{synchroniseds._mutex...} | |
{} | |
using TupleOf = std::tuple<Ts&...>; | |
template <size_t I> | |
constexpr std::tuple_element<I, SynchroniseAll>::type& get() { return std::get<I>(_data); } | |
private: | |
TupleOf _data; | |
std::scoped_lock<typename Synchronised<Ts>::Mutex...> _lock; | |
}; | |
// TODO: See if there's a way to put these nested inside SynchroniseAll class... | |
template <typename... Ts> | |
struct std::tuple_size<SynchroniseAll<Ts...>> { | |
static constexpr size_t value = std::tuple_size<typename SynchroniseAll<Ts...>::TupleOf>::value; | |
}; | |
template <size_t I, typename... Ts> | |
struct std::tuple_element<I, SynchroniseAll<Ts...>> : std::tuple_element<I, typename SynchroniseAll<Ts...>::TupleOf> | |
{}; | |
struct GarkWunkle { | |
int blaim; | |
float dreebs_per_bloom; | |
unsigned vinder_nunkin; | |
}; | |
int main() { | |
Synchronised<int> foo = 43; | |
Synchronised<int> bar = -991; | |
Synchronised<float> baz = 42.19975f; | |
{ | |
auto [foo_val, bar_val, baz_val] = SynchroniseAll(foo, bar, baz); | |
foo_val += bar_val - baz_val; | |
} | |
Synchronised<GarkWunkle> tally_bunger{{123, -45.46f, 110u}}; | |
tally_bunger.seize()->blaim -= 42; | |
return tally_bunger.get().blaim; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment