1046 lines
50 KiB
C++
1046 lines
50 KiB
C++
#pragma once
|
|
#include "type_descriptor.hpp"
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <concepts>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <initializer_list>
|
|
#include <memory>
|
|
#include <limits>
|
|
#include <mutex>
|
|
#include <shared_mutex>
|
|
#include <span>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <typeinfo>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
namespace structive {
|
|
struct Null_Shared_Mutex {
|
|
void lock() {}
|
|
void unlock() {}
|
|
void lock_shared() {}
|
|
void unlock_shared() {}
|
|
};
|
|
struct Shared_Mutex_Policy {
|
|
using mutex_type = std::shared_mutex;
|
|
};
|
|
struct No_Lock_Policy {
|
|
using mutex_type = Null_Shared_Mutex;
|
|
};
|
|
template <class Mutex>
|
|
concept Shared_Lockable = requires(Mutex& mutex) {
|
|
mutex.lock();
|
|
mutex.unlock();
|
|
mutex.lock_shared();
|
|
mutex.unlock_shared();
|
|
};
|
|
template <class Policy>
|
|
concept Synchronization_Policy = requires {
|
|
typename Policy::mutex_type;
|
|
} && Shared_Lockable<typename Policy::mutex_type>;
|
|
namespace detail {
|
|
template <class Mutex, bool Stores_Mutexes = !std::same_as<Mutex, Null_Shared_Mutex>>
|
|
class Mutex_Storage;
|
|
template <class Mutex>
|
|
class Mutex_Storage<Mutex, true> {
|
|
std::unique_ptr<Mutex[]> mutexes_;
|
|
public:
|
|
Mutex_Storage() = default;
|
|
explicit Mutex_Storage(std::size_t count) : mutexes_(count ? std::make_unique<Mutex[]>(count) : nullptr) {}
|
|
void reset(std::size_t count) {
|
|
mutexes_ = count ? std::make_unique<Mutex[]>(count) : nullptr;
|
|
}
|
|
Mutex& get(std::size_t index) noexcept {
|
|
return mutexes_[index];
|
|
}
|
|
Mutex& get(std::size_t index) const noexcept {
|
|
return mutexes_[index];
|
|
}
|
|
};
|
|
template <class Mutex>
|
|
class Mutex_Storage<Mutex, false> {
|
|
static Mutex& mutex() noexcept {
|
|
static Mutex value;
|
|
return value;
|
|
}
|
|
public:
|
|
Mutex_Storage() = default;
|
|
explicit Mutex_Storage(std::size_t) {}
|
|
void reset(std::size_t) {}
|
|
Mutex& get(std::size_t) noexcept {
|
|
return mutex();
|
|
}
|
|
Mutex& get(std::size_t) const noexcept {
|
|
return mutex();
|
|
}
|
|
};
|
|
}
|
|
enum class Managed_Access_Mode {
|
|
internal,
|
|
external,
|
|
persistence
|
|
};
|
|
enum class Runtime_Access_Result {
|
|
ok,
|
|
unknown_property,
|
|
not_readable,
|
|
not_writable,
|
|
type_mismatch
|
|
};
|
|
using Runtime_Read_Callback = void (*)(void*, std::size_t, std::string_view, const std::type_info&, const void*);
|
|
class Property_Object_Base {
|
|
struct Runtime_Interface {
|
|
const std::type_info& (*object_type)() noexcept;
|
|
std::size_t (*property_count)() noexcept;
|
|
Runtime_Access_Result (*read)(const Property_Object_Base&, Managed_Access_Mode, std::string_view, void*, Runtime_Read_Callback);
|
|
Runtime_Access_Result (*write)(Property_Object_Base&, Managed_Access_Mode, std::string_view, const std::type_info&, const void*);
|
|
};
|
|
const Runtime_Interface* runtime_interface_{};
|
|
protected:
|
|
explicit Property_Object_Base(const Runtime_Interface* runtime_interface) noexcept : runtime_interface_(runtime_interface) {}
|
|
Property_Object_Base(const Property_Object_Base&) noexcept = default;
|
|
Property_Object_Base(Property_Object_Base&&) noexcept = default;
|
|
Property_Object_Base& operator=(const Property_Object_Base&) noexcept = default;
|
|
Property_Object_Base& operator=(Property_Object_Base&&) noexcept = default;
|
|
~Property_Object_Base() = default;
|
|
template <class Object, Synchronization_Policy Policy>
|
|
friend class Property_Object;
|
|
public:
|
|
const std::type_info& runtime_object_type() const noexcept {
|
|
return runtime_interface_->object_type();
|
|
}
|
|
std::size_t runtime_property_count() const noexcept {
|
|
return runtime_interface_->property_count();
|
|
}
|
|
Runtime_Access_Result runtime_read(Managed_Access_Mode mode, std::string_view key, void* context, Runtime_Read_Callback callback) const {
|
|
return runtime_interface_->read(*this, mode, key, context, callback);
|
|
}
|
|
Runtime_Access_Result runtime_write(Managed_Access_Mode mode, std::string_view key, const std::type_info& value_type, const void* value) {
|
|
return runtime_interface_->write(*this, mode, key, value_type, value);
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
|
|
inline constexpr bool property_read_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::readable : Mode == Managed_Access_Mode::external ? external_readable_v<Schema, Index> : persistence_storable_v<Schema, Index>;
|
|
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
|
|
inline constexpr bool property_write_allowed_v = Mode == Managed_Access_Mode::internal ? Schema::template property_type<Index>::writable : Mode == Managed_Access_Mode::external ? external_writable_v<Schema, Index> : persistence_loadable_v<Schema, Index>;
|
|
template <Managed_Access_Mode Mode, class Schema, std::size_t Index>
|
|
inline constexpr bool property_visible_v = property_read_allowed_v<Mode, Schema, Index> || property_write_allowed_v<Mode, Schema, Index>;
|
|
struct Property_Synchronization {
|
|
Synchronization_Plan plan;
|
|
};
|
|
inline Property_Synchronization property_synchronization(Synchronization_Plan plan) {
|
|
return {std::move(plan)};
|
|
}
|
|
template <class Object, Synchronization_Source_Type Source>
|
|
Property_Synchronization property_synchronization(Source&& source) requires Property_Described_Object<Object> {
|
|
using Schema = type_descriptor_schema_t<Object>;
|
|
return {materialize_synchronization_plan<Schema>(std::forward<Source>(source))};
|
|
}
|
|
struct Resolved_Synchronization_View {
|
|
static constexpr std::size_t unsynchronized_slot = std::numeric_limits<std::size_t>::max();
|
|
std::span<const std::size_t> lock_slots;
|
|
std::size_t lock_count{};
|
|
std::size_t slot(std::size_t index) const noexcept {
|
|
return lock_slots[index];
|
|
}
|
|
bool uses_lock(std::size_t index) const noexcept {
|
|
return slot(index) != unsynchronized_slot;
|
|
}
|
|
};
|
|
template <class Derived, Synchronization_Policy Lock_Policy = Shared_Mutex_Policy>
|
|
class Property_Object : public Property_Object_Base {
|
|
public:
|
|
using object_type = Derived;
|
|
using mutex_type = typename Lock_Policy::mutex_type;
|
|
private:
|
|
static constexpr bool uses_real_mutexes = !std::same_as<mutex_type, Null_Shared_Mutex>;
|
|
struct Empty_Guard_Locks {};
|
|
struct Dynamic_Lock_Targets {
|
|
std::vector<std::size_t> slots;
|
|
std::vector<std::size_t> unsynchronized_properties;
|
|
};
|
|
template <std::size_t Capacity>
|
|
struct Static_Lock_Targets {
|
|
std::array<std::size_t, Capacity> slots{};
|
|
std::size_t slot_count{};
|
|
std::array<std::size_t, Capacity> unsynchronized_properties{};
|
|
std::size_t unsynchronized_count{};
|
|
};
|
|
std::unique_ptr<std::size_t[]> custom_lock_layout_;
|
|
[[no_unique_address]] detail::Mutex_Storage<mutex_type> mutex_storage_;
|
|
static const auto& default_resolved_synchronization() {
|
|
static const auto resolved = resolve_synchronization_plan(type_descriptor<Derived>(), type_descriptor<Derived>().synchronization_plan());
|
|
return resolved;
|
|
}
|
|
template <class Schema>
|
|
void initialize(const Schema& schema, const Synchronization_Plan& plan) {
|
|
auto resolved = resolve_synchronization_plan(schema, plan);
|
|
auto layout = std::make_unique<std::size_t[]>(Schema::property_count + 1);
|
|
layout[0] = resolved.lock_count;
|
|
std::copy(resolved.lock_slots.begin(), resolved.lock_slots.end(), layout.get() + 1);
|
|
custom_lock_layout_ = std::move(layout);
|
|
mutex_storage_.reset(resolved.lock_count);
|
|
}
|
|
void copy_synchronization_from(const Property_Object& other) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
if (other.custom_lock_layout_) {
|
|
auto layout = std::make_unique<std::size_t[]>(Schema::property_count + 1);
|
|
std::copy_n(other.custom_lock_layout_.get(), Schema::property_count + 1, layout.get());
|
|
custom_lock_layout_ = std::move(layout);
|
|
} else {
|
|
custom_lock_layout_.reset();
|
|
}
|
|
mutex_storage_.reset(lock_count());
|
|
}
|
|
std::size_t lock_count() const noexcept {
|
|
return custom_lock_layout_ ? custom_lock_layout_[0] : default_resolved_synchronization().lock_count;
|
|
}
|
|
std::size_t slot(std::size_t index) const noexcept {
|
|
return custom_lock_layout_ ? custom_lock_layout_[index + 1] : default_resolved_synchronization().slot(index);
|
|
}
|
|
mutex_type& mutex(std::size_t index) noexcept {
|
|
return mutex_storage_.get(index);
|
|
}
|
|
mutex_type& mutex(std::size_t index) const noexcept {
|
|
return mutex_storage_.get(index);
|
|
}
|
|
template <std::size_t Capacity>
|
|
static void sort_unique_prefix(std::array<std::size_t, Capacity>& values, std::size_t& count) {
|
|
for (std::size_t index = 1; index < count; ++index) {
|
|
auto value = values[index];
|
|
auto position = index;
|
|
while (position > 0 && value < values[position - 1]) {
|
|
values[position] = values[position - 1];
|
|
--position;
|
|
}
|
|
values[position] = value;
|
|
}
|
|
if (count == 0) {
|
|
return;
|
|
}
|
|
std::size_t write = 1;
|
|
for (std::size_t read = 1; read < count; ++read) {
|
|
if (values[read] != values[write - 1]) {
|
|
values[write++] = values[read];
|
|
}
|
|
}
|
|
count = write;
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
static bool runtime_property_readable(std::size_t index) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static const auto table = []<std::size_t... Index>(std::index_sequence<Index...>) {
|
|
return std::array<bool, Schema::property_count>{property_read_allowed_v<Mode, Schema, Index>...};
|
|
}(std::make_index_sequence<Schema::property_count>{});
|
|
return table[index];
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
static bool runtime_property_visible(std::size_t index) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static const auto table = []<std::size_t... Index>(std::index_sequence<Index...>) {
|
|
return std::array<bool, Schema::property_count>{property_visible_v<Mode, Schema, Index>...};
|
|
}(std::make_index_sequence<Schema::property_count>{});
|
|
return table[index];
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Dynamic_Lock_Targets collect_dynamic_lock_targets(std::span<const std::string_view> keys, bool shared_access) const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
Dynamic_Lock_Targets targets;
|
|
targets.slots.reserve(keys.size());
|
|
targets.unsynchronized_properties.reserve(keys.size());
|
|
for (auto key : keys) {
|
|
auto index = schema_property_index<Schema>(key);
|
|
if (!index) {
|
|
throw std::invalid_argument("Unknown property: " + std::string(key));
|
|
}
|
|
bool allowed = shared_access ? runtime_property_readable<Mode>(*index) : runtime_property_visible<Mode>(*index);
|
|
if (!allowed) {
|
|
throw std::invalid_argument("Property is not accessible through this view: " + std::string(key));
|
|
}
|
|
auto lock_slot = slot(*index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
targets.unsynchronized_properties.push_back(*index);
|
|
} else {
|
|
targets.slots.push_back(lock_slot);
|
|
}
|
|
}
|
|
std::sort(targets.slots.begin(), targets.slots.end());
|
|
targets.slots.erase(std::unique(targets.slots.begin(), targets.slots.end()), targets.slots.end());
|
|
std::sort(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end());
|
|
targets.unsynchronized_properties.erase(std::unique(targets.unsynchronized_properties.begin(), targets.unsynchronized_properties.end()), targets.unsynchronized_properties.end());
|
|
return targets;
|
|
}
|
|
template <Managed_Access_Mode Mode, bool Shared_Access, std::size_t... Index>
|
|
Static_Lock_Targets<sizeof...(Index)> collect_static_lock_targets() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(((Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_visible_v<Mode, Schema, Index>) && ...));
|
|
Static_Lock_Targets<sizeof...(Index)> targets;
|
|
auto collect = [&]<std::size_t Property_Index>() {
|
|
auto lock_slot = slot(Property_Index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
targets.unsynchronized_properties[targets.unsynchronized_count++] = Property_Index;
|
|
} else {
|
|
targets.slots[targets.slot_count++] = lock_slot;
|
|
}
|
|
};
|
|
(collect.template operator()<Index>(), ...);
|
|
sort_unique_prefix(targets.slots, targets.slot_count);
|
|
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
|
|
return targets;
|
|
}
|
|
template <Managed_Access_Mode Mode, bool Shared_Access>
|
|
auto collect_all_static_lock_targets() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
Static_Lock_Targets<Schema::property_count> targets;
|
|
auto collect = [&]<std::size_t Index>() {
|
|
constexpr bool allowed = Shared_Access ? property_read_allowed_v<Mode, Schema, Index> : property_write_allowed_v<Mode, Schema, Index>;
|
|
if constexpr (allowed) {
|
|
auto lock_slot = slot(Index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
targets.unsynchronized_properties[targets.unsynchronized_count++] = Index;
|
|
} else {
|
|
targets.slots[targets.slot_count++] = lock_slot;
|
|
}
|
|
}
|
|
};
|
|
[&]<std::size_t... Index>(std::index_sequence<Index...>) {
|
|
(collect.template operator()<Index>(), ...);
|
|
}(std::make_index_sequence<Schema::property_count>{});
|
|
sort_unique_prefix(targets.slots, targets.slot_count);
|
|
sort_unique_prefix(targets.unsynchronized_properties, targets.unsynchronized_count);
|
|
return targets;
|
|
}
|
|
template <std::size_t Index, class View>
|
|
decltype(auto) read_unlocked(const View& view) const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
const auto& descriptor = type_descriptor<Derived>().template property<Index>();
|
|
using Accessor = typename Schema::template property_type<Index>::accessor_type;
|
|
if constexpr (Accessor::synchronized_view_read) {
|
|
return descriptor.accessor.read(view);
|
|
} else {
|
|
return descriptor.accessor.read(static_cast<const Derived&>(*this));
|
|
}
|
|
}
|
|
template <std::size_t Index, class Value>
|
|
void write_unlocked(Value&& value) {
|
|
const auto& descriptor = type_descriptor<Derived>().template property<Index>();
|
|
descriptor.accessor.write(static_cast<Derived&>(*this), std::forward<Value>(value));
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
class Single_Read_View {
|
|
const Property_Object* owner_{};
|
|
std::size_t property_index_{};
|
|
std::size_t lock_slot_{};
|
|
public:
|
|
Single_Read_View(const Property_Object& owner, std::size_t property_index, std::size_t lock_slot) : owner_(&owner), property_index_(property_index), lock_slot_(lock_slot) {}
|
|
template <auto Member>
|
|
decltype(auto) get() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
if (owner_->slot(index) != lock_slot_ || (lock_slot_ == Resolved_Synchronization_View::unsynchronized_slot && index != property_index_)) {
|
|
throw std::logic_error("Computed property reads outside its synchronization slot");
|
|
}
|
|
return owner_->template read_unlocked<index>(*this);
|
|
}
|
|
template <Fixed_String Key>
|
|
decltype(auto) get_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
if (owner_->slot(index) != lock_slot_ || (lock_slot_ == Resolved_Synchronization_View::unsynchronized_slot && index != property_index_)) {
|
|
throw std::logic_error("Computed property reads outside its synchronization slot");
|
|
}
|
|
return owner_->template read_unlocked<index>(*this);
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode, std::size_t Index>
|
|
auto read_one() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(property_read_allowed_v<Mode, Schema, Index>);
|
|
using Property = typename Schema::template property_type<Index>;
|
|
using Value = typename Property::value_type;
|
|
if constexpr (!uses_real_mutexes && !Property::accessor_type::synchronized_view_read) {
|
|
return Value(read_unlocked<Index>(*this));
|
|
}
|
|
auto lock_slot = slot(Index);
|
|
Single_Read_View<Mode> view{*this, Index, lock_slot};
|
|
if constexpr (!uses_real_mutexes) {
|
|
return Value(read_unlocked<Index>(view));
|
|
}
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
return Value(read_unlocked<Index>(view));
|
|
}
|
|
std::shared_lock lock{mutex(lock_slot)};
|
|
return Value(read_unlocked<Index>(view));
|
|
}
|
|
template <Managed_Access_Mode Mode, std::size_t Index, class Value>
|
|
void write_one(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(property_write_allowed_v<Mode, Schema, Index>);
|
|
if constexpr (!uses_real_mutexes) {
|
|
write_unlocked<Index>(std::forward<Value>(value));
|
|
return;
|
|
}
|
|
auto lock_slot = slot(Index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
write_unlocked<Index>(std::forward<Value>(value));
|
|
return;
|
|
}
|
|
std::unique_lock lock{mutex(lock_slot)};
|
|
write_unlocked<Index>(std::forward<Value>(value));
|
|
}
|
|
public:
|
|
template <Managed_Access_Mode Mode>
|
|
class Read_Guard {
|
|
const Property_Object* owner_{};
|
|
std::vector<std::size_t> slots_;
|
|
std::vector<std::size_t> unsynchronized_properties_;
|
|
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::vector<std::shared_lock<mutex_type>>, Empty_Guard_Locks> locks_;
|
|
bool holds(std::size_t index) const {
|
|
auto lock_slot = owner_->slot(index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
return std::binary_search(unsynchronized_properties_.begin(), unsynchronized_properties_.end(), index);
|
|
}
|
|
return std::binary_search(slots_.begin(), slots_.end(), lock_slot);
|
|
}
|
|
friend class Property_Object;
|
|
Read_Guard(const Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
|
|
if constexpr (uses_real_mutexes) {
|
|
locks_.reserve(slots_.size());
|
|
for (auto lock_slot : slots_) {
|
|
locks_.emplace_back(owner_->mutex(lock_slot));
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
template <std::size_t Index>
|
|
decltype(auto) get_index() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_read_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
return owner_->template read_unlocked<Index>(*this);
|
|
}
|
|
template <auto Member>
|
|
decltype(auto) get() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
decltype(auto) get_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode>
|
|
class Write_Guard {
|
|
Property_Object* owner_{};
|
|
std::vector<std::size_t> slots_;
|
|
std::vector<std::size_t> unsynchronized_properties_;
|
|
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::vector<std::unique_lock<mutex_type>>, Empty_Guard_Locks> locks_;
|
|
bool holds(std::size_t index) const {
|
|
auto lock_slot = owner_->slot(index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
return std::binary_search(unsynchronized_properties_.begin(), unsynchronized_properties_.end(), index);
|
|
}
|
|
return std::binary_search(slots_.begin(), slots_.end(), lock_slot);
|
|
}
|
|
friend class Property_Object;
|
|
Write_Guard(Property_Object& owner, Dynamic_Lock_Targets targets) : owner_(&owner), slots_(std::move(targets.slots)), unsynchronized_properties_(std::move(targets.unsynchronized_properties)) {
|
|
if constexpr (uses_real_mutexes) {
|
|
locks_.reserve(slots_.size());
|
|
for (auto lock_slot : slots_) {
|
|
locks_.emplace_back(owner_->mutex(lock_slot));
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
template <std::size_t Index>
|
|
decltype(auto) get_index() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_read_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
return owner_->template read_unlocked<Index>(*this);
|
|
}
|
|
template <auto Member>
|
|
decltype(auto) get() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
decltype(auto) get_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <std::size_t Index, class Value>
|
|
void set_index(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_write_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
owner_->template write_unlocked<Index>(std::forward<Value>(value));
|
|
}
|
|
template <auto Member, class Value>
|
|
void set(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
set_index<index>(std::forward<Value>(value));
|
|
}
|
|
template <Fixed_String Key, class Value>
|
|
void set_key(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
set_index<index>(std::forward<Value>(value));
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode, std::size_t Capacity>
|
|
class Static_Read_Guard {
|
|
const Property_Object* owner_{};
|
|
Static_Lock_Targets<Capacity> targets_;
|
|
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::array<std::shared_lock<mutex_type>, Capacity>, Empty_Guard_Locks> locks_{};
|
|
bool holds(std::size_t index) const {
|
|
auto lock_slot = owner_->slot(index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
return std::binary_search(targets_.unsynchronized_properties.begin(), targets_.unsynchronized_properties.begin() + static_cast<std::ptrdiff_t>(targets_.unsynchronized_count), index);
|
|
}
|
|
return std::binary_search(targets_.slots.begin(), targets_.slots.begin() + static_cast<std::ptrdiff_t>(targets_.slot_count), lock_slot);
|
|
}
|
|
friend class Property_Object;
|
|
Static_Read_Guard(const Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
|
|
if constexpr (uses_real_mutexes) {
|
|
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
|
|
locks_[index] = std::shared_lock<mutex_type>{owner_->mutex(targets_.slots[index])};
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
template <std::size_t Index>
|
|
decltype(auto) get_index() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_read_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
return owner_->template read_unlocked<Index>(*this);
|
|
}
|
|
template <auto Member>
|
|
decltype(auto) get() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
decltype(auto) get_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode, std::size_t Capacity>
|
|
class Static_Write_Guard {
|
|
Property_Object* owner_{};
|
|
Static_Lock_Targets<Capacity> targets_;
|
|
[[no_unique_address]] std::conditional_t<uses_real_mutexes, std::array<std::unique_lock<mutex_type>, Capacity>, Empty_Guard_Locks> locks_{};
|
|
bool holds(std::size_t index) const {
|
|
auto lock_slot = owner_->slot(index);
|
|
if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
return std::binary_search(targets_.unsynchronized_properties.begin(), targets_.unsynchronized_properties.begin() + static_cast<std::ptrdiff_t>(targets_.unsynchronized_count), index);
|
|
}
|
|
return std::binary_search(targets_.slots.begin(), targets_.slots.begin() + static_cast<std::ptrdiff_t>(targets_.slot_count), lock_slot);
|
|
}
|
|
friend class Property_Object;
|
|
Static_Write_Guard(Property_Object& owner, Static_Lock_Targets<Capacity> targets) : owner_(&owner), targets_(std::move(targets)) {
|
|
if constexpr (uses_real_mutexes) {
|
|
for (std::size_t index = 0; index < targets_.slot_count; ++index) {
|
|
locks_[index] = std::unique_lock<mutex_type>{owner_->mutex(targets_.slots[index])};
|
|
}
|
|
}
|
|
}
|
|
public:
|
|
template <std::size_t Index>
|
|
decltype(auto) get_index() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_read_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
return owner_->template read_unlocked<Index>(*this);
|
|
}
|
|
template <auto Member>
|
|
decltype(auto) get() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
decltype(auto) get_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return get_index<index>();
|
|
}
|
|
template <std::size_t Index, class Value>
|
|
void set_index(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert(Index < Schema::property_count);
|
|
static_assert(property_write_allowed_v<Mode, Schema, Index>);
|
|
if (!holds(Index)) {
|
|
throw std::logic_error("Property is outside the held synchronization set");
|
|
}
|
|
owner_->template write_unlocked<Index>(std::forward<Value>(value));
|
|
}
|
|
template <auto Member, class Value>
|
|
void set(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
set_index<index>(std::forward<Value>(value));
|
|
}
|
|
template <Fixed_String Key, class Value>
|
|
void set_key(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
set_index<index>(std::forward<Value>(value));
|
|
}
|
|
};
|
|
private:
|
|
template <Managed_Access_Mode Mode, class Function>
|
|
void for_each_readable_impl(Function&& function) const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
const auto& schema = type_descriptor<Derived>();
|
|
schema.for_each_property([&](auto index, const auto& descriptor) {
|
|
constexpr std::size_t property_index = decltype(index)::value;
|
|
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
|
|
auto value = read_one<Mode, property_index>();
|
|
std::invoke(function, index, descriptor, value);
|
|
}
|
|
});
|
|
}
|
|
template <Managed_Access_Mode Mode, class Function>
|
|
void for_each_readable_locked_impl(Function&& function) const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
auto targets = collect_all_static_lock_targets<Mode, true>();
|
|
Static_Read_Guard<Mode, Schema::property_count> guard{*this, std::move(targets)};
|
|
type_descriptor<Derived>().for_each_property([&](auto index, const auto& descriptor) {
|
|
constexpr std::size_t property_index = decltype(index)::value;
|
|
if constexpr (property_read_allowed_v<Mode, Schema, property_index>) {
|
|
std::invoke(function, index, descriptor, guard.template get_index<property_index>());
|
|
}
|
|
});
|
|
}
|
|
template <Managed_Access_Mode Mode, class Function>
|
|
void with_all_writable_locked_impl(Function&& function) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
auto targets = collect_all_static_lock_targets<Mode, false>();
|
|
Static_Write_Guard<Mode, Schema::property_count> guard{*this, std::move(targets)};
|
|
std::invoke(std::forward<Function>(function), guard);
|
|
}
|
|
static const std::type_info& runtime_object_type_impl() noexcept {
|
|
return typeid(Derived);
|
|
}
|
|
static std::size_t runtime_property_count_impl() noexcept {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
return Schema::property_count;
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Runtime_Access_Result runtime_read_mode(std::string_view key, void* context, Runtime_Read_Callback callback) const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
auto index = schema_property_index<Schema>(key);
|
|
if (!index) {
|
|
return Runtime_Access_Result::unknown_property;
|
|
}
|
|
if (!runtime_property_readable<Mode>(*index)) {
|
|
return Runtime_Access_Result::not_readable;
|
|
}
|
|
Runtime_Access_Result result = Runtime_Access_Result::unknown_property;
|
|
visit_schema_property(type_descriptor<Derived>(), key, [&](auto property_index_constant, const auto&) {
|
|
constexpr std::size_t property_index = decltype(property_index_constant)::value;
|
|
using Value = typename Schema::template property_type<property_index>::value_type;
|
|
auto lock_slot = slot(property_index);
|
|
Single_Read_View<Mode> view{*this, property_index, lock_slot};
|
|
auto emit = [&] {
|
|
if constexpr (std::is_reference_v<decltype(read_unlocked<property_index>(view))>) {
|
|
auto&& value = read_unlocked<property_index>(view);
|
|
callback(context, property_index, key, typeid(Value), std::addressof(value));
|
|
} else {
|
|
Value value = read_unlocked<property_index>(view);
|
|
callback(context, property_index, key, typeid(Value), std::addressof(value));
|
|
}
|
|
};
|
|
if constexpr (!uses_real_mutexes) {
|
|
emit();
|
|
} else if (lock_slot == Resolved_Synchronization_View::unsynchronized_slot) {
|
|
emit();
|
|
} else {
|
|
std::shared_lock lock{mutex(lock_slot)};
|
|
emit();
|
|
}
|
|
result = Runtime_Access_Result::ok;
|
|
});
|
|
return result;
|
|
}
|
|
static Runtime_Access_Result runtime_read_impl(const Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, void* context, Runtime_Read_Callback callback) {
|
|
const auto& self = static_cast<const Property_Object&>(base);
|
|
switch (mode) {
|
|
case Managed_Access_Mode::internal:
|
|
return self.template runtime_read_mode<Managed_Access_Mode::internal>(key, context, callback);
|
|
case Managed_Access_Mode::external:
|
|
return self.template runtime_read_mode<Managed_Access_Mode::external>(key, context, callback);
|
|
case Managed_Access_Mode::persistence:
|
|
return self.template runtime_read_mode<Managed_Access_Mode::persistence>(key, context, callback);
|
|
}
|
|
return Runtime_Access_Result::not_readable;
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Runtime_Access_Result runtime_write_mode(std::string_view key, const std::type_info& value_type, const void* value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
auto index = schema_property_index<Schema>(key);
|
|
if (!index) {
|
|
return Runtime_Access_Result::unknown_property;
|
|
}
|
|
if (!runtime_property_visible<Mode>(*index)) {
|
|
return Runtime_Access_Result::not_writable;
|
|
}
|
|
Runtime_Access_Result result = Runtime_Access_Result::unknown_property;
|
|
visit_schema_property(type_descriptor<Derived>(), key, [&](auto property_index_constant, const auto&) {
|
|
constexpr std::size_t property_index = decltype(property_index_constant)::value;
|
|
using Property = typename Schema::template property_type<property_index>;
|
|
using Accessor = typename Property::accessor_type;
|
|
using Value = typename Property::value_type;
|
|
if constexpr (!property_write_allowed_v<Mode, Schema, property_index> || !requires(const Accessor& accessor, Derived& object, const Value& candidate) { accessor.write(object, candidate); }) {
|
|
result = Runtime_Access_Result::not_writable;
|
|
} else if (value_type != typeid(Value)) {
|
|
result = Runtime_Access_Result::type_mismatch;
|
|
} else {
|
|
write_one<Mode, property_index>(*static_cast<const Value*>(value));
|
|
result = Runtime_Access_Result::ok;
|
|
}
|
|
});
|
|
return result;
|
|
}
|
|
static Runtime_Access_Result runtime_write_impl(Property_Object_Base& base, Managed_Access_Mode mode, std::string_view key, const std::type_info& value_type, const void* value) {
|
|
auto& self = static_cast<Property_Object&>(base);
|
|
switch (mode) {
|
|
case Managed_Access_Mode::internal:
|
|
return self.template runtime_write_mode<Managed_Access_Mode::internal>(key, value_type, value);
|
|
case Managed_Access_Mode::external:
|
|
return self.template runtime_write_mode<Managed_Access_Mode::external>(key, value_type, value);
|
|
case Managed_Access_Mode::persistence:
|
|
return self.template runtime_write_mode<Managed_Access_Mode::persistence>(key, value_type, value);
|
|
}
|
|
return Runtime_Access_Result::not_writable;
|
|
}
|
|
static const Property_Object_Base::Runtime_Interface* runtime_interface() noexcept {
|
|
static const Property_Object_Base::Runtime_Interface value{
|
|
&runtime_object_type_impl,
|
|
&runtime_property_count_impl,
|
|
&runtime_read_impl,
|
|
&runtime_write_impl
|
|
};
|
|
return &value;
|
|
}
|
|
protected:
|
|
Property_Object() : Property_Object_Base(runtime_interface()), mutex_storage_(default_resolved_synchronization().lock_count) {}
|
|
explicit Property_Object(Property_Synchronization synchronization) : Property_Object_Base(runtime_interface()) {
|
|
initialize(type_descriptor<Derived>(), synchronization.plan);
|
|
}
|
|
Property_Object(const Property_Object& other) : Property_Object_Base(runtime_interface()) {
|
|
copy_synchronization_from(other);
|
|
}
|
|
Property_Object(Property_Object&& other) : Property_Object_Base(runtime_interface()) {
|
|
copy_synchronization_from(other);
|
|
}
|
|
Property_Object& operator=(const Property_Object&) noexcept {
|
|
return *this;
|
|
}
|
|
Property_Object& operator=(Property_Object&&) noexcept {
|
|
return *this;
|
|
}
|
|
~Property_Object() = default;
|
|
public:
|
|
const auto& schema() const noexcept {
|
|
return type_descriptor<Derived>();
|
|
}
|
|
Resolved_Synchronization_View resolved_synchronization() const noexcept {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
if (custom_lock_layout_) {
|
|
return {{custom_lock_layout_.get() + 1, Schema::property_count}, custom_lock_layout_[0]};
|
|
}
|
|
const auto& resolved = default_resolved_synchronization();
|
|
return {resolved.lock_slots, resolved.lock_count};
|
|
}
|
|
Derived& unsafe_object() noexcept {
|
|
return static_cast<Derived&>(*this);
|
|
}
|
|
const Derived& unsafe_object() const noexcept {
|
|
return static_cast<const Derived&>(*this);
|
|
}
|
|
template <auto Member>
|
|
auto read() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return read_one<Managed_Access_Mode::internal, index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
auto read_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return read_one<Managed_Access_Mode::internal, index>();
|
|
}
|
|
template <auto Member, class Value>
|
|
void write(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
|
|
}
|
|
template <Fixed_String Key, class Value>
|
|
void write_key(Value&& value) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
write_one<Managed_Access_Mode::internal, index>(std::forward<Value>(value));
|
|
}
|
|
template <auto Member>
|
|
std::size_t lock_slot() const noexcept {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return slot(index);
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
|
|
return Read_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, true)};
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Read_Guard<Mode> lock_shared(std::initializer_list<std::string_view> keys) const {
|
|
return lock_shared<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
|
|
}
|
|
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::span<const std::string_view> keys) const {
|
|
return lock_shared<Managed_Access_Mode::internal>(keys);
|
|
}
|
|
Read_Guard<Managed_Access_Mode::internal> lock_shared(std::initializer_list<std::string_view> keys) const {
|
|
return lock_shared<Managed_Access_Mode::internal>(keys);
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
|
|
return Write_Guard<Mode>{*this, collect_dynamic_lock_targets<Mode>(keys, false)};
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
Write_Guard<Mode> lock_unique(std::initializer_list<std::string_view> keys) {
|
|
return lock_unique<Mode>(std::span<const std::string_view>{keys.begin(), keys.size()});
|
|
}
|
|
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::span<const std::string_view> keys) {
|
|
return lock_unique<Managed_Access_Mode::internal>(keys);
|
|
}
|
|
Write_Guard<Managed_Access_Mode::internal> lock_unique(std::initializer_list<std::string_view> keys) {
|
|
return lock_unique<Managed_Access_Mode::internal>(keys);
|
|
}
|
|
template <Managed_Access_Mode Mode, auto... Members>
|
|
auto lock_shared_mode() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert((Schema_Property_Member<Schema, Members> && ...));
|
|
auto targets = collect_static_lock_targets<Mode, true, schema_member_property_index_v<Schema, Members>...>();
|
|
return Static_Read_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
|
|
}
|
|
template <auto... Members>
|
|
auto lock_shared() const {
|
|
return lock_shared_mode<Managed_Access_Mode::internal, Members...>();
|
|
}
|
|
template <Managed_Access_Mode Mode, auto... Members>
|
|
auto lock_unique_mode() {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
static_assert((Schema_Property_Member<Schema, Members> && ...));
|
|
auto targets = collect_static_lock_targets<Mode, false, schema_member_property_index_v<Schema, Members>...>();
|
|
return Static_Write_Guard<Mode, sizeof...(Members)>{*this, std::move(targets)};
|
|
}
|
|
template <auto... Members>
|
|
auto lock_unique() {
|
|
return lock_unique_mode<Managed_Access_Mode::internal, Members...>();
|
|
}
|
|
template <class Function>
|
|
void for_each_readable(Function&& function) const {
|
|
for_each_readable_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void for_each_readable_locked(Function&& function) const {
|
|
for_each_readable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void with_all_writable_locked(Function&& function) {
|
|
with_all_writable_locked_impl<Managed_Access_Mode::internal>(std::forward<Function>(function));
|
|
}
|
|
template <Managed_Access_Mode Mode>
|
|
class Capability_View {
|
|
Property_Object* owner_{};
|
|
public:
|
|
explicit Capability_View(Property_Object& owner) : owner_(&owner) {}
|
|
template <auto Member>
|
|
auto read() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return owner_->template read_one<Mode, index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
auto read_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return owner_->template read_one<Mode, index>();
|
|
}
|
|
template <auto Member, class Value>
|
|
void write(Value&& value) requires (Mode != Managed_Access_Mode::persistence) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
owner_->template write_one<Mode, index>(std::forward<Value>(value));
|
|
}
|
|
template <Fixed_String Key, class Value>
|
|
void write_key(Value&& value) requires (Mode != Managed_Access_Mode::persistence) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
owner_->template write_one<Mode, index>(std::forward<Value>(value));
|
|
}
|
|
template <auto Member, class Value>
|
|
void load(Value&& value) requires (Mode == Managed_Access_Mode::persistence) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
owner_->template write_one<Mode, index>(std::forward<Value>(value));
|
|
}
|
|
template <Fixed_String Key, class Value>
|
|
void load_key(Value&& value) requires (Mode == Managed_Access_Mode::persistence) {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
owner_->template write_one<Mode, index>(std::forward<Value>(value));
|
|
}
|
|
template <auto Member>
|
|
auto store() const requires (Mode == Managed_Access_Mode::persistence) {
|
|
return read<Member>();
|
|
}
|
|
template <Fixed_String Key>
|
|
auto store_key() const requires (Mode == Managed_Access_Mode::persistence) {
|
|
return read_key<Key>();
|
|
}
|
|
template <auto... Members>
|
|
auto lock_shared() const {
|
|
return owner_->template lock_shared_mode<Mode, Members...>();
|
|
}
|
|
template <auto... Members>
|
|
auto lock_unique() {
|
|
return owner_->template lock_unique_mode<Mode, Members...>();
|
|
}
|
|
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
|
|
return owner_->template lock_shared<Mode>(keys);
|
|
}
|
|
Write_Guard<Mode> lock_unique(std::span<const std::string_view> keys) {
|
|
return owner_->template lock_unique<Mode>(keys);
|
|
}
|
|
template <class Function>
|
|
void for_each_readable(Function&& function) const {
|
|
owner_->template for_each_readable_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void for_each_readable_locked(Function&& function) const {
|
|
owner_->template for_each_readable_locked_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void with_all_writable_locked(Function&& function) requires (Mode != Managed_Access_Mode::persistence) {
|
|
owner_->template with_all_writable_locked_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void with_all_loadable_locked(Function&& function) requires (Mode == Managed_Access_Mode::persistence) {
|
|
owner_->template with_all_writable_locked_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
};
|
|
template <Managed_Access_Mode Mode>
|
|
class Const_Capability_View {
|
|
const Property_Object* owner_{};
|
|
public:
|
|
explicit Const_Capability_View(const Property_Object& owner) : owner_(&owner) {}
|
|
template <auto Member>
|
|
auto read() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_member_property_index_v<Schema, Member>;
|
|
static_assert(index < Schema::property_count);
|
|
return owner_->template read_one<Mode, index>();
|
|
}
|
|
template <Fixed_String Key>
|
|
auto read_key() const {
|
|
using Schema = type_descriptor_schema_t<Derived>;
|
|
constexpr auto index = schema_property_index_v<Schema, Key>;
|
|
static_assert(index < Schema::property_count);
|
|
return owner_->template read_one<Mode, index>();
|
|
}
|
|
template <auto Member>
|
|
auto store() const requires (Mode == Managed_Access_Mode::persistence) {
|
|
return read<Member>();
|
|
}
|
|
template <Fixed_String Key>
|
|
auto store_key() const requires (Mode == Managed_Access_Mode::persistence) {
|
|
return read_key<Key>();
|
|
}
|
|
template <auto... Members>
|
|
auto lock_shared() const {
|
|
return owner_->template lock_shared_mode<Mode, Members...>();
|
|
}
|
|
Read_Guard<Mode> lock_shared(std::span<const std::string_view> keys) const {
|
|
return owner_->template lock_shared<Mode>(keys);
|
|
}
|
|
template <class Function>
|
|
void for_each_readable(Function&& function) const {
|
|
owner_->template for_each_readable_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
template <class Function>
|
|
void for_each_readable_locked(Function&& function) const {
|
|
owner_->template for_each_readable_locked_impl<Mode>(std::forward<Function>(function));
|
|
}
|
|
};
|
|
Capability_View<Managed_Access_Mode::external> external() {
|
|
return Capability_View<Managed_Access_Mode::external>{*this};
|
|
}
|
|
Capability_View<Managed_Access_Mode::persistence> persistence() {
|
|
return Capability_View<Managed_Access_Mode::persistence>{*this};
|
|
}
|
|
Const_Capability_View<Managed_Access_Mode::external> external() const {
|
|
return Const_Capability_View<Managed_Access_Mode::external>{*this};
|
|
}
|
|
Const_Capability_View<Managed_Access_Mode::persistence> persistence() const {
|
|
return Const_Capability_View<Managed_Access_Mode::persistence>{*this};
|
|
}
|
|
};
|
|
}
|