416 lines
18 KiB
C++
416 lines
18 KiB
C++
#pragma once
|
|
#include "adminive/descriptor.hpp"
|
|
#include <structive/property/property.hpp>
|
|
#include <array>
|
|
#include <concepts>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <ranges>
|
|
#include <span>
|
|
#include <string_view>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
namespace adminive {
|
|
template <class Mutex>
|
|
struct Mutex_Policy {
|
|
using mutex_type = Mutex;
|
|
};
|
|
template <class T, structive::Synchronization_Policy Policy = structive::Shared_Mutex_Policy>
|
|
class Managed_Value;
|
|
template <class Managed, std::size_t Root_Index, auto... Members>
|
|
class Managed_Field;
|
|
namespace detail {
|
|
template <class T>
|
|
struct Is_Reference_Wrapper : std::false_type {};
|
|
template <class T>
|
|
struct Is_Reference_Wrapper<std::reference_wrapper<T>> : std::true_type {};
|
|
template <class Result>
|
|
concept Managed_Callback_Result = std::is_void_v<Result> || (!std::is_reference_v<Result> && !std::is_pointer_v<Result> && !Is_Reference_Wrapper<std::remove_cvref_t<Result>>::value && !std::ranges::view<std::remove_cvref_t<Result>>);
|
|
template <std::size_t Value>
|
|
consteval std::size_t decimal_digits() {
|
|
std::size_t result = 1;
|
|
std::size_t current = Value;
|
|
while(current >= 10) {
|
|
current /= 10;
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
template <std::size_t Index>
|
|
consteval auto make_managed_key_text() {
|
|
constexpr std::string_view prefix = "__adminive_";
|
|
constexpr std::size_t digits = decimal_digits<Index>();
|
|
std::array<char, prefix.size() + digits> result{};
|
|
for(std::size_t position = 0; position < prefix.size(); ++position) {
|
|
result[position] = prefix[position];
|
|
}
|
|
std::size_t value = Index;
|
|
for(std::size_t position = 0; position < digits; ++position) {
|
|
result[prefix.size() + digits - position - 1] = static_cast<char>('0' + value % 10);
|
|
value /= 10;
|
|
}
|
|
return result;
|
|
}
|
|
template <std::size_t Index>
|
|
struct Managed_Key_Value {
|
|
static constexpr auto text = make_managed_key_text<Index>();
|
|
constexpr std::string_view view() const noexcept {
|
|
return {text.data(), text.size()};
|
|
}
|
|
};
|
|
template <std::size_t Index>
|
|
struct Managed_Key_Attribute {
|
|
using attribute_category = structive::Key_Category;
|
|
static constexpr bool single_valued = true;
|
|
static constexpr bool inheritable = false;
|
|
static constexpr Managed_Key_Value<Index> value{};
|
|
};
|
|
template <class T>
|
|
consteval bool member_type_has_managed_writes() {
|
|
using Value = std::remove_cvref_t<T>;
|
|
if constexpr(Described_Type<Value>) {
|
|
return described_type_has_managed_writes<Value>();
|
|
}
|
|
return false;
|
|
}
|
|
template <class Field>
|
|
inline constexpr bool managed_field_writable_v = Field::managed_writable || member_type_has_managed_writes<typename Field::member_type>();
|
|
template <class Field>
|
|
consteval bool field_requires_synchronization();
|
|
template <class T>
|
|
consteval bool member_type_requires_synchronization() {
|
|
using Value = std::remove_cvref_t<T>;
|
|
if constexpr(Described_Type<Value>) {
|
|
using Fields = Descriptor_Fields<Value>;
|
|
return []<std::size_t... Indexes>(std::index_sequence<Indexes...>) {
|
|
return (field_requires_synchronization<std::tuple_element_t<Indexes, Fields>>() || ...);
|
|
}(std::make_index_sequence<std::tuple_size_v<Fields>>{});
|
|
}
|
|
return false;
|
|
}
|
|
template <class Field>
|
|
consteval bool field_requires_synchronization() {
|
|
if constexpr(Field::managed_writable) {
|
|
return Field::synchronized;
|
|
}
|
|
return member_type_requires_synchronization<typename Field::member_type>();
|
|
}
|
|
template <class T, std::size_t Index>
|
|
const auto& managed_descriptor_field() {
|
|
static const auto descriptor = describe<T>();
|
|
return std::get<Index>(descriptor.fields());
|
|
}
|
|
template <class Managed, class Field, std::size_t Index>
|
|
struct Managed_Field_Accessor {
|
|
using object_type = Managed;
|
|
using value_type = typename Field::member_type;
|
|
struct storage_identity {};
|
|
using dependency_spec = structive::No_Property_Dependencies;
|
|
static constexpr bool readable = true;
|
|
static constexpr bool writable = managed_field_writable_v<Field>;
|
|
static constexpr bool synchronized_view_read = false;
|
|
static constexpr bool trusted_object_access = false;
|
|
decltype(auto) read(const object_type& object) const
|
|
noexcept(noexcept(managed_descriptor_field<typename Managed::value_type, Index>().get(
|
|
object.unsafe_value()))) {
|
|
return managed_descriptor_field<typename Managed::value_type, Index>().get(
|
|
object.unsafe_value());
|
|
}
|
|
decltype(auto) read(object_type& object) const
|
|
noexcept(noexcept(managed_descriptor_field<typename Managed::value_type, Index>().get(
|
|
object.unsafe_value()))) {
|
|
return managed_descriptor_field<typename Managed::value_type, Index>().get(
|
|
object.unsafe_value());
|
|
}
|
|
template <class Value>
|
|
void write(object_type& object, Value&& value) const
|
|
requires writable && requires {
|
|
managed_descriptor_field<typename Managed::value_type, Index>().set(
|
|
object.unsafe_value(), std::forward<Value>(value));
|
|
} {
|
|
managed_descriptor_field<typename Managed::value_type, Index>().set(
|
|
object.unsafe_value(), std::forward<Value>(value));
|
|
}
|
|
};
|
|
template <class Managed, class Field, std::size_t Index>
|
|
auto make_structive_property() {
|
|
using Accessor = Managed_Field_Accessor<Managed, Field, Index>;
|
|
using Key = Managed_Key_Attribute<Index>;
|
|
if constexpr(managed_field_writable_v<Field>) {
|
|
using Capability = structive::Property_Capability_Attribute<structive::Property_Capability::read_write>;
|
|
return structive::Property_Descriptor<Accessor, Key, Capability>{{}, {Key{}, Capability{}}};
|
|
} else {
|
|
using Capability = structive::Property_Capability_Attribute<structive::Property_Capability::read>;
|
|
return structive::Property_Descriptor<Accessor, Key, Capability>{{}, {Key{}, Capability{}}};
|
|
}
|
|
}
|
|
template <class Managed, class T, std::size_t... Indexes>
|
|
auto make_managed_schema_impl(std::index_sequence<Indexes...>) {
|
|
using Fields = Descriptor_Fields<T>;
|
|
structive::Synchronization_Plan plan;
|
|
([&] {
|
|
using Field = std::tuple_element_t<Indexes, Fields>;
|
|
if constexpr(managed_field_writable_v<Field> && !field_requires_synchronization<Field>()) {
|
|
plan.unsynchronized(Managed_Key_Attribute<Indexes>::value.view());
|
|
}
|
|
}(), ...);
|
|
return structive::object<Managed>(std::move(plan), make_structive_property<Managed, std::tuple_element_t<Indexes, Fields>, Indexes>()...);
|
|
}
|
|
template <class Managed, Described_Type T>
|
|
auto make_managed_schema() {
|
|
return make_managed_schema_impl<Managed, T>(std::make_index_sequence<std::tuple_size_v<Descriptor_Fields<T>>>{});
|
|
}
|
|
template <Described_Type T, auto Member, std::size_t Index = 0>
|
|
consteval std::size_t described_member_index() {
|
|
using Fields = Descriptor_Fields<T>;
|
|
if constexpr(Index == std::tuple_size_v<Fields>) {
|
|
return Index;
|
|
} else {
|
|
using Field = std::tuple_element_t<Index, Fields>;
|
|
using Accessor = typename Field::accessor_type;
|
|
if constexpr(requires { Accessor::member; } && std::same_as<std::remove_cv_t<decltype(Accessor::member)>, decltype(Member)>) {
|
|
if constexpr(Accessor::member == Member) {
|
|
return Index;
|
|
}
|
|
}
|
|
return described_member_index<T, Member, Index + 1>();
|
|
}
|
|
}
|
|
template <class Current, auto Member, auto... Rest>
|
|
consteval bool managed_member_path_writable() {
|
|
static_assert(Described_Type<std::remove_cvref_t<Current>>);
|
|
constexpr auto index = described_member_index<std::remove_cvref_t<Current>, Member>();
|
|
using Field = Descriptor_Field<std::remove_cvref_t<Current>, index>;
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
return Field::managed_writable;
|
|
} else {
|
|
return managed_member_path_writable<typename Field::member_type, Rest...>();
|
|
}
|
|
}
|
|
template <class Current, auto Member, auto... Rest>
|
|
consteval bool managed_member_path_requires_synchronization() {
|
|
static_assert(Described_Type<std::remove_cvref_t<Current>>);
|
|
constexpr auto index = described_member_index<std::remove_cvref_t<Current>, Member>();
|
|
using Field = Descriptor_Field<std::remove_cvref_t<Current>, index>;
|
|
if constexpr(Field::managed_writable) {
|
|
return Field::synchronized;
|
|
} else if constexpr(sizeof...(Rest) == 0) {
|
|
return false;
|
|
} else {
|
|
return managed_member_path_requires_synchronization<typename Field::member_type, Rest...>();
|
|
}
|
|
}
|
|
template <class Root, std::size_t Root_Index, auto... Members>
|
|
consteval bool managed_path_writable() {
|
|
using Field = Descriptor_Field<Root, Root_Index>;
|
|
if constexpr(sizeof...(Members) == 0) {
|
|
return Field::managed_writable;
|
|
} else {
|
|
return managed_member_path_writable<typename Field::member_type, Members...>();
|
|
}
|
|
}
|
|
template <class Root, std::size_t Root_Index, auto... Members>
|
|
consteval bool managed_path_requires_synchronization() {
|
|
using Field = Descriptor_Field<Root, Root_Index>;
|
|
if constexpr(Field::managed_writable) {
|
|
return Field::synchronized;
|
|
} else if constexpr(sizeof...(Members) == 0) {
|
|
return false;
|
|
} else {
|
|
return managed_member_path_requires_synchronization<typename Field::member_type, Members...>();
|
|
}
|
|
}
|
|
template <class Object, auto Member, auto... Rest>
|
|
decltype(auto) path_value(Object& object) {
|
|
auto&& next = object.*Member;
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
return std::forward<decltype(next)>(next);
|
|
} else {
|
|
return path_value<decltype(next), Rest...>(next);
|
|
}
|
|
}
|
|
template <class Object, auto Member, auto... Rest>
|
|
decltype(auto) path_value(const Object& object) {
|
|
auto&& next = object.*Member;
|
|
if constexpr(sizeof...(Rest) == 0) {
|
|
return std::forward<decltype(next)>(next);
|
|
} else {
|
|
return path_value<decltype(next), Rest...>(next);
|
|
}
|
|
}
|
|
template <class T, std::size_t Root_Index, auto... Members>
|
|
decltype(auto) managed_path_value(T& object) {
|
|
using Field = Descriptor_Field<T, Root_Index>;
|
|
auto&& root = managed_descriptor_field<T, Root_Index>().get(object);
|
|
if constexpr(sizeof...(Members) == 0) {
|
|
return std::forward<decltype(root)>(root);
|
|
} else {
|
|
return path_value<decltype(root), Members...>(root);
|
|
}
|
|
}
|
|
template <class T, std::size_t Root_Index, auto... Members>
|
|
decltype(auto) managed_path_value(const T& object) {
|
|
using Field = Descriptor_Field<T, Root_Index>;
|
|
auto&& root = managed_descriptor_field<T, Root_Index>().get(object);
|
|
if constexpr(sizeof...(Members) == 0) {
|
|
return std::forward<decltype(root)>(root);
|
|
} else {
|
|
return path_value<decltype(root), Members...>(root);
|
|
}
|
|
}
|
|
template <class T, std::size_t... Indexes>
|
|
auto managed_key_array_impl(std::index_sequence<Indexes...>) {
|
|
return std::array<std::string_view, sizeof...(Indexes)>{Managed_Key_Attribute<Indexes>::value.view()...};
|
|
}
|
|
template <Described_Type T>
|
|
const auto& managed_key_array() {
|
|
static const auto value = managed_key_array_impl<T>(std::make_index_sequence<std::tuple_size_v<Descriptor_Fields<T>>>{});
|
|
return value;
|
|
}
|
|
}
|
|
}
|
|
namespace structive {
|
|
template <class T, Synchronization_Policy Policy>
|
|
struct Type_Descriptor<adminive::Managed_Value<T, Policy>> {
|
|
static auto get() {
|
|
return adminive::detail::make_managed_schema<adminive::Managed_Value<T, Policy>, T>();
|
|
}
|
|
};
|
|
}
|
|
namespace adminive {
|
|
template <class T, structive::Synchronization_Policy Policy>
|
|
class Managed_Value : public structive::Property_Object<Managed_Value<T, Policy>, Policy> {
|
|
using Base = structive::Property_Object<Managed_Value<T, Policy>, Policy>;
|
|
public:
|
|
using value_type = T;
|
|
using policy_type = Policy;
|
|
Managed_Value() requires std::default_initializable<T> = default;
|
|
explicit Managed_Value(T value) : value_(std::move(value)) {}
|
|
explicit Managed_Value(structive::Property_Synchronization synchronization) : Base(std::move(synchronization)) {}
|
|
Managed_Value(T value, structive::Property_Synchronization synchronization) : Base(std::move(synchronization)), value_(std::move(value)) {}
|
|
Managed_Value(const Managed_Value&) requires std::copy_constructible<T> = default;
|
|
Managed_Value(Managed_Value&&) noexcept(std::is_nothrow_move_constructible_v<T>) requires std::move_constructible<T> = default;
|
|
Managed_Value& operator=(const Managed_Value&) requires std::is_copy_assignable_v<T> = default;
|
|
Managed_Value& operator=(Managed_Value&&) noexcept(std::is_nothrow_move_assignable_v<T>) requires std::is_move_assignable_v<T> = default;
|
|
T& unsafe_value() noexcept {
|
|
return value_;
|
|
}
|
|
const T& unsafe_value() const noexcept {
|
|
return value_;
|
|
}
|
|
template <class Function>
|
|
decltype(auto) read(Function&& function) const requires std::invocable<Function, const T&> && detail::Managed_Callback_Result<std::invoke_result_t<Function, const T&>> {
|
|
return this->with_all_readable_locked([&](const auto&) -> decltype(auto) {
|
|
return std::invoke(std::forward<Function>(function), std::as_const(value_));
|
|
});
|
|
}
|
|
template <class Function>
|
|
decltype(auto) write(Function&& function) requires std::invocable<Function, T&> && detail::Managed_Callback_Result<std::invoke_result_t<Function, T&>> {
|
|
using Result = std::invoke_result_t<Function, T&>;
|
|
if constexpr(std::is_void_v<Result>) {
|
|
this->with_all_writable_locked([&](auto&) {
|
|
std::invoke(std::forward<Function>(function), value_);
|
|
});
|
|
} else {
|
|
using Value_Result = std::remove_cvref_t<Result>;
|
|
std::optional<Value_Result> result;
|
|
this->with_all_writable_locked([&](auto&) {
|
|
result.emplace(std::invoke(std::forward<Function>(function), value_));
|
|
});
|
|
return Value_Result(std::move(*result));
|
|
}
|
|
}
|
|
T snapshot() const requires std::copy_constructible<T> {
|
|
return read([](const T& value) {
|
|
return value;
|
|
});
|
|
}
|
|
template <auto Member>
|
|
auto member() {
|
|
constexpr auto index = detail::described_member_index<T, Member>();
|
|
static_assert(index < std::tuple_size_v<Descriptor_Fields<T>>);
|
|
return Managed_Field<Managed_Value, index>{*this};
|
|
}
|
|
template <auto Member>
|
|
auto member() const {
|
|
constexpr auto index = detail::described_member_index<T, Member>();
|
|
static_assert(index < std::tuple_size_v<Descriptor_Fields<T>>);
|
|
return Managed_Field<const Managed_Value, index>{*this};
|
|
}
|
|
template <std::size_t Index>
|
|
auto field() {
|
|
static_assert(Index < std::tuple_size_v<Descriptor_Fields<T>>);
|
|
return Managed_Field<Managed_Value, Index>{*this};
|
|
}
|
|
template <std::size_t Index>
|
|
auto field() const {
|
|
static_assert(Index < std::tuple_size_v<Descriptor_Fields<T>>);
|
|
return Managed_Field<const Managed_Value, Index>{*this};
|
|
}
|
|
private:
|
|
T value_{};
|
|
};
|
|
template <class Managed, std::size_t Root_Index, auto... Members>
|
|
class Managed_Field {
|
|
using Owner = std::remove_const_t<Managed>;
|
|
using Root = typename Owner::value_type;
|
|
public:
|
|
using owner_type = Managed;
|
|
using value_type = std::remove_cvref_t<decltype(detail::managed_path_value<Root, Root_Index, Members...>(std::declval<Root&>()))>;
|
|
explicit Managed_Field(Managed& owner) : owner_(&owner) {}
|
|
template <class Function>
|
|
decltype(auto) read(Function&& function) const requires std::invocable<Function, const value_type&> && detail::Managed_Callback_Result<std::invoke_result_t<Function, const value_type&>> {
|
|
return owner_->template with_read_index<Root_Index>([&](const auto& root_value) -> decltype(auto) {
|
|
if constexpr(sizeof...(Members) == 0)
|
|
return std::invoke(std::forward<Function>(function), root_value);
|
|
else
|
|
return std::invoke(std::forward<Function>(function), detail::path_value<decltype(root_value), Members...>(root_value));
|
|
});
|
|
}
|
|
template <class Function>
|
|
decltype(auto) write(Function&& function) requires (!std::is_const_v<Managed>) && (detail::managed_path_writable<Root, Root_Index, Members...>()) && std::invocable<Function, value_type&> && detail::Managed_Callback_Result<std::invoke_result_t<Function, value_type&>> {
|
|
if constexpr(detail::managed_path_requires_synchronization<Root, Root_Index, Members...>()) {
|
|
const auto key = detail::Managed_Key_Attribute<Root_Index>::value.view();
|
|
auto guard = owner_->lock_unique({key});
|
|
auto& value = detail::managed_path_value<Root, Root_Index, Members...>(owner_->unsafe_value());
|
|
return std::invoke(std::forward<Function>(function), value);
|
|
} else {
|
|
auto& value = detail::managed_path_value<Root, Root_Index, Members...>(owner_->unsafe_value());
|
|
return std::invoke(std::forward<Function>(function), value);
|
|
}
|
|
}
|
|
value_type snapshot() const requires std::copy_constructible<value_type> {
|
|
return read([](const value_type& value) {
|
|
return value;
|
|
});
|
|
}
|
|
template <auto Member>
|
|
auto member() requires (!std::is_const_v<Managed>) {
|
|
return Managed_Field<Managed, Root_Index, Members..., Member>{*owner_};
|
|
}
|
|
template <auto Member>
|
|
auto member() const {
|
|
return Managed_Field<const Owner, Root_Index, Members..., Member>{*owner_};
|
|
}
|
|
Managed& owner() const noexcept {
|
|
return *owner_;
|
|
}
|
|
private:
|
|
Managed* owner_;
|
|
};
|
|
template <class T>
|
|
struct Is_Managed_Value : std::false_type {};
|
|
template <class T, structive::Synchronization_Policy Policy>
|
|
struct Is_Managed_Value<Managed_Value<T, Policy>> : std::true_type {};
|
|
template <class T>
|
|
concept Managed_Value_Type = Is_Managed_Value<std::remove_cvref_t<T>>::value;
|
|
template <class T>
|
|
struct Is_Managed_Field : std::false_type {};
|
|
template <class Managed, std::size_t Root_Index, auto... Members>
|
|
struct Is_Managed_Field<Managed_Field<Managed, Root_Index, Members...>> : std::true_type {};
|
|
template <class T>
|
|
concept Managed_Field_Type = Is_Managed_Field<std::remove_cvref_t<T>>::value;
|
|
}
|