更新接口

This commit is contained in:
2026-08-07 23:44:35 +08:00
parent f9fcac46dc
commit a5041e10bc
22 changed files with 976 additions and 316 deletions
+2
View File
@@ -6,6 +6,7 @@ add_library(Adminive::Core ALIAS Adminive)
add_library(Adminive::Http ALIAS Adminive)
target_include_directories(Adminive INTERFACE "$<BUILD_INTERFACE:${adminive_library_include_dir}>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_compile_features(Adminive INTERFACE cxx_std_20)
target_link_libraries(Adminive INTERFACE "$<BUILD_INTERFACE:structive::property_core>")
if(MSVC)
target_compile_options(Adminive INTERFACE /utf-8 /Zc:__cplusplus)
if(ADMINIVE_PROPAGATE_MSVC_STRICT_MODE)
@@ -22,3 +23,4 @@ if(BUILD_TESTING)
endif()
install(TARGETS Adminive EXPORT AdminiveCoreTargets)
install(DIRECTORY "${adminive_library_include_dir}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install(DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/../../third_party/Structive/core/include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
@@ -7,5 +7,5 @@
#include "adminive/http.hpp"
#include "adminive/json.hpp"
#include "adminive/status.hpp"
#include "adminive/synchronized.hpp"
#include "adminive/managed.hpp"
#include "adminive/value.hpp"
+6 -6
View File
@@ -459,15 +459,15 @@ Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::strin
}
return result;
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
Json to_amis_form_schema(const Synchronized_Value<T, Lock, Field_Lock>& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.read([&](const T& item) {
template <Json_Type Json, Managed_Value_Type T>
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, std::move(submit_api), std::move(submit_label));
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
Json to_amis_form_schema(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.object_read([&](const auto& item) {
template <Json_Type Json, Managed_Field_Type T>
Json to_amis_form_schema(const T& value, std::string submit_api = {}, std::string submit_label = "Apply") {
return value.read([&](const auto& item) {
return adminive::to_amis_form_schema<Json>(item, std::move(submit_api), std::move(submit_label));
});
}
+161 -137
View File
@@ -12,11 +12,6 @@
#include <type_traits>
#include <utility>
namespace adminive {
enum class Field_Lock_Mode {
automatic,
enabled,
disabled
};
template <class T>
struct Type_Descriptor;
template <class T>
@@ -39,6 +34,7 @@ template <auto Member>
struct Member_Field_Accessor {
using owner_type = typename Member_Pointer_Traits<Member>::owner_type;
using member_type = typename Member_Pointer_Traits<Member>::member_type;
static constexpr auto member = Member;
template <class Object>
static decltype(auto) get(Object& object) noexcept {
return object.*Member;
@@ -54,6 +50,7 @@ struct Reflected_Field_Accessor {
using reference_type = decltype(Reflection_Adapter<T>::template get<Index>(std::declval<T&>()));
using const_reference_type = decltype(Reflection_Adapter<T>::template get<Index>(std::declval<const T&>()));
using member_type = std::remove_cvref_t<reference_type>;
static constexpr std::size_t index = Index;
static_assert(std::is_lvalue_reference_v<reference_type>);
static_assert(std::is_lvalue_reference_v<const_reference_type>);
static decltype(auto) get(T& object) noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
@@ -83,8 +80,8 @@ inline std::string make_label(std::string_view name) {
std::string result;
result.reserve(name.size());
bool uppercase = true;
for (const char value : name) {
if (value == '_') {
for(const char value : name) {
if(value == '_') {
result.push_back(' ');
uppercase = true;
continue;
@@ -94,111 +91,142 @@ inline std::string make_label(std::string_view name) {
}
return result;
}
template <class Derived, class Accessor>
class Field_Descriptor_Base {
struct Field_Metadata {
std::string name;
std::string label;
std::string list_label;
std::string description;
std::string widget;
std::string visible_on;
std::map<std::string, std::string> enum_labels;
bool editable{};
bool creatable{};
bool readable{true};
bool sensitive{};
bool include_default{true};
bool visible{true};
bool required{};
bool list_visible{true};
int order{-1};
bool sortable{};
};
template <class Accessor, bool Managed_Writable = false, bool Synchronized = true>
class Field_Descriptor {
public:
using field_descriptor_tag = void;
using accessor_type = Accessor;
using owner_type = typename Accessor::owner_type;
using member_type = typename Accessor::member_type;
explicit Field_Descriptor_Base(std::string name) : name_(std::move(name)), label_(make_label(name_)) {}
Field_Descriptor_Base(std::string name, std::string label) : name_(std::move(name)), label_(std::move(label)) {}
Derived editable(bool value = true) const {
auto result = derived();
result.editable_ = value;
static constexpr bool managed_writable = Managed_Writable;
static constexpr bool synchronized = Synchronized;
explicit Field_Descriptor(std::string name) {
metadata_.name = std::move(name);
metadata_.label = make_label(metadata_.name);
}
Field_Descriptor(std::string name, std::string label) {
metadata_.name = std::move(name);
metadata_.label = std::move(label);
}
explicit Field_Descriptor(Field_Metadata metadata) : metadata_(std::move(metadata)) {}
auto editable() const {
auto result = rebind<true, Synchronized>();
result.metadata_.editable = true;
return result;
}
Derived locked(bool value = true) const {
auto result = derived();
result.lock_mode_ = value ? Field_Lock_Mode::enabled : Field_Lock_Mode::disabled;
auto creatable() const {
auto result = rebind<true, Synchronized>();
result.metadata_.creatable = true;
return result;
}
Derived creatable(bool value = true) const {
auto result = derived();
result.creatable_ = value;
auto read_write() const {
return rebind<true, Synchronized>();
}
auto unsynchronized() const {
return rebind<Managed_Writable, false>();
}
Field_Descriptor readable(bool value = true) const {
auto result = *this;
result.metadata_.readable = value;
return result;
}
Derived readable(bool value = true) const {
auto result = derived();
result.readable_ = value;
return result;
}
Derived sensitive(bool value = true) const {
auto result = derived();
result.sensitive_ = value;
Field_Descriptor sensitive(bool value = true) const {
auto result = *this;
result.metadata_.sensitive = value;
if(value) {
result.readable_ = false;
result.include_default_ = false;
result.metadata_.readable = false;
result.metadata_.include_default = false;
}
return result;
}
Derived include_default(bool value = true) const {
auto result = derived();
result.include_default_ = value;
Field_Descriptor include_default(bool value = true) const {
auto result = *this;
result.metadata_.include_default = value;
return result;
}
Derived visible(bool value = true) const {
auto result = derived();
result.visible_ = value;
Field_Descriptor visible(bool value = true) const {
auto result = *this;
result.metadata_.visible = value;
return result;
}
Derived required(bool value = true) const {
auto result = derived();
result.required_ = value;
Field_Descriptor required(bool value = true) const {
auto result = *this;
result.metadata_.required = value;
return result;
}
Derived list_visible(bool value = true) const {
auto result = derived();
result.list_visible_ = value;
Field_Descriptor list_visible(bool value = true) const {
auto result = *this;
result.metadata_.list_visible = value;
return result;
}
Derived label(std::string value) const {
auto result = derived();
result.label_ = std::move(value);
Field_Descriptor label(std::string value) const {
auto result = *this;
result.metadata_.label = std::move(value);
return result;
}
Derived description(std::string value) const {
auto result = derived();
result.description_ = std::move(value);
Field_Descriptor description(std::string value) const {
auto result = *this;
result.metadata_.description = std::move(value);
return result;
}
Derived widget(std::string value) const {
auto result = derived();
result.widget_ = std::move(value);
Field_Descriptor widget(std::string value) const {
auto result = *this;
result.metadata_.widget = std::move(value);
return result;
}
Derived visible_on(std::string value) const {
auto result = derived();
result.visible_on_ = std::move(value);
Field_Descriptor visible_on(std::string value) const {
auto result = *this;
result.metadata_.visible_on = std::move(value);
return result;
}
Derived list_label(std::string value) const {
auto result = derived();
result.list_label_ = std::move(value);
Field_Descriptor list_label(std::string value) const {
auto result = *this;
result.metadata_.list_label = std::move(value);
return result;
}
Derived order(int value) const {
auto result = derived();
result.order_ = value;
Field_Descriptor order(int value) const {
auto result = *this;
result.metadata_.order = value;
return result;
}
Derived sortable(bool value = true) const {
auto result = derived();
result.sortable_ = value;
Field_Descriptor sortable(bool value = true) const {
auto result = *this;
result.metadata_.sortable = value;
return result;
}
template <auto Value>
requires Enum_Type<std::remove_cv_t<decltype(Value)>>
Derived enum_label(std::string label) const {
Field_Descriptor enum_label(std::string label) const {
const auto name = enum_name(Value);
if (name.empty()) {
if(name.empty()) {
throw std::invalid_argument("enum adapter returned an empty value name");
}
auto result = derived();
result.enum_labels_.insert_or_assign(std::string(name), std::move(label));
auto result = *this;
result.metadata_.enum_labels.insert_or_assign(std::string(name), std::move(label));
return result;
}
Derived enum_label(std::string value_name, std::string label) const {
auto result = derived();
result.enum_labels_.insert_or_assign(std::move(value_name), std::move(label));
Field_Descriptor enum_label(std::string value_name, std::string label) const {
auto result = *this;
result.metadata_.enum_labels.insert_or_assign(std::move(value_name), std::move(label));
return result;
}
template <class Object>
@@ -212,112 +240,81 @@ public:
return Accessor::get(object);
}
const std::string& name() const noexcept {
return name_;
return metadata_.name;
}
const std::string& label() const noexcept {
return label_;
return metadata_.label;
}
const std::string& list_label() const noexcept {
return list_label_.empty() ? label_ : list_label_;
return metadata_.list_label.empty() ? metadata_.label : metadata_.list_label;
}
const std::string& description() const noexcept {
return description_;
return metadata_.description;
}
const std::string& widget() const noexcept {
return widget_;
return metadata_.widget;
}
const std::string& visible_on() const noexcept {
return visible_on_;
return metadata_.visible_on;
}
const std::map<std::string, std::string>& enum_labels() const noexcept {
return enum_labels_;
return metadata_.enum_labels;
}
bool is_editable() const noexcept {
return editable_;
}
Field_Lock_Mode lock_mode() const noexcept {
return lock_mode_;
return metadata_.editable;
}
bool is_creatable() const noexcept {
return creatable_;
return metadata_.creatable;
}
bool is_readable() const noexcept {
return readable_;
return metadata_.readable;
}
bool is_sensitive() const noexcept {
return sensitive_;
return metadata_.sensitive;
}
bool includes_default() const noexcept {
return include_default_;
return metadata_.include_default;
}
bool is_visible() const noexcept {
return visible_;
return metadata_.visible;
}
bool is_required() const noexcept {
return required_;
return metadata_.required;
}
int order() const noexcept {
return order_;
return metadata_.order;
}
bool is_sortable() const noexcept {
return sortable_;
return metadata_.sortable;
}
bool is_list_visible() const noexcept {
return list_visible_;
}
protected:
const Derived& derived() const noexcept {
return static_cast<const Derived&>(*this);
return metadata_.list_visible;
}
private:
std::string name_;
std::string label_;
std::string list_label_;
std::string description_;
std::string widget_;
std::string visible_on_;
std::map<std::string, std::string> enum_labels_;
bool editable_{};
Field_Lock_Mode lock_mode_{Field_Lock_Mode::automatic};
bool creatable_{};
bool readable_{true};
bool sensitive_{};
bool include_default_{true};
bool visible_{true};
bool required_{};
bool list_visible_{true};
int order_{-1};
bool sortable_{};
};
template <auto Member>
class Field_Descriptor : public Field_Descriptor_Base<Field_Descriptor<Member>, Member_Field_Accessor<Member>> {
public:
using Base = Field_Descriptor_Base<Field_Descriptor<Member>, Member_Field_Accessor<Member>>;
using Base::Base;
static constexpr auto member = Member;
};
template <class T, std::size_t Index>
class Reflected_Field_Descriptor : public Field_Descriptor_Base<Reflected_Field_Descriptor<T, Index>, Reflected_Field_Accessor<T, Index>> {
public:
using Base = Field_Descriptor_Base<Reflected_Field_Descriptor<T, Index>, Reflected_Field_Accessor<T, Index>>;
using Base::Base;
static constexpr std::size_t index = Index;
template <bool New_Writable, bool New_Synchronized>
auto rebind() const {
return Field_Descriptor<Accessor, New_Writable, New_Synchronized>(metadata_);
}
template <class, bool, bool>
friend class Field_Descriptor;
Field_Metadata metadata_;
};
template <class T>
struct Is_Field_Descriptor : std::false_type {};
template <auto Member>
struct Is_Field_Descriptor<Field_Descriptor<Member>> : std::true_type {};
template <class T, std::size_t Index>
struct Is_Field_Descriptor<Reflected_Field_Descriptor<T, Index>> : std::true_type {};
template <class T>
concept Field_Descriptor_Type = Is_Field_Descriptor<std::remove_cvref_t<T>>::value;
concept Field_Descriptor_Type = requires {
typename std::remove_cvref_t<T>::field_descriptor_tag;
typename std::remove_cvref_t<T>::owner_type;
typename std::remove_cvref_t<T>::member_type;
typename std::remove_cvref_t<T>::accessor_type;
{ std::remove_cvref_t<T>::managed_writable } -> std::convertible_to<bool>;
{ std::remove_cvref_t<T>::synchronized } -> std::convertible_to<bool>;
};
template <auto Member>
auto field(std::string name) {
return Field_Descriptor<Member>(std::move(name));
return Field_Descriptor<Member_Field_Accessor<Member>>(std::move(name));
}
template <auto Member>
auto field(std::string name, std::string label) {
return Field_Descriptor<Member>(std::move(name), std::move(label));
return Field_Descriptor<Member_Field_Accessor<Member>>(std::move(name), std::move(label));
}
template <Reflected_Type T, std::size_t Index>
auto reflected_field() {
@@ -326,7 +323,7 @@ auto reflected_field() {
if(name.empty()) {
throw std::invalid_argument("reflected field name must not be empty");
}
return Reflected_Field_Descriptor<T, Index>(name);
return Field_Descriptor<Reflected_Field_Accessor<T, Index>>(name);
}
template <Reflected_Type T, std::size_t Index>
auto reflected_field(std::string label) {
@@ -335,7 +332,7 @@ auto reflected_field(std::string label) {
if(name.empty()) {
throw std::invalid_argument("reflected field name must not be empty");
}
return Reflected_Field_Descriptor<T, Index>(name, std::move(label));
return Field_Descriptor<Reflected_Field_Accessor<T, Index>>(name, std::move(label));
}
struct No_Object_Validator {
template <class T>
@@ -361,6 +358,7 @@ class Object_Descriptor {
public:
using object_type = T;
using validator_type = Validator;
using fields_type = std::tuple<Fields...>;
Object_Descriptor(std::string name, std::string label, Validator validator, std::tuple<Fields...> fields, Object_List_Options list_options = {}) : name_(std::move(name)), label_(std::move(label)), validator_(std::move(validator)), fields_(std::move(fields)), list_options_(std::move(list_options)) {}
template <class New_Validator>
auto validator(New_Validator value) const {
@@ -538,6 +536,32 @@ auto describe() {
validate_descriptor(descriptor);
return descriptor;
}
template <class T>
using Descriptor_Type = std::remove_cvref_t<decltype(Type_Descriptor<std::remove_cvref_t<T>>::get())>;
template <class T>
using Descriptor_Fields = typename Descriptor_Type<T>::fields_type;
template <class T, std::size_t Index>
using Descriptor_Field = std::tuple_element_t<Index, Descriptor_Fields<T>>;
template <Described_Type T>
consteval bool described_type_has_managed_writes();
template <class Field>
consteval bool field_has_managed_writes() {
if constexpr(Field::managed_writable) {
return true;
} else if constexpr(Described_Type<typename Field::member_type>) {
return described_type_has_managed_writes<typename Field::member_type>();
} else {
return false;
}
}
template <Described_Type T, std::size_t... Indexes>
consteval bool described_type_has_managed_writes_impl(std::index_sequence<Indexes...>) {
return (field_has_managed_writes<Descriptor_Field<T, Indexes>>() || ...);
}
template <Described_Type T>
consteval bool described_type_has_managed_writes() {
return described_type_has_managed_writes_impl<T>(std::make_index_sequence<std::tuple_size_v<Descriptor_Fields<T>>>{});
}
}
#define ADMINIVE_FIELD(Type, Member) ::adminive::field<&Type::Member>(#Member)
#define ADMINIVE_FIELD_LABEL(Type, Member, Label) ::adminive::field<&Type::Member>(#Member, Label)
+10 -18
View File
@@ -1,12 +1,12 @@
#pragma once
#include "adminive/amis.hpp"
#include "adminive/synchronized.hpp"
#include "adminive/lock.hpp"
#include "adminive/managed.hpp"
#include <exception>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <utility>
namespace adminive {
@@ -50,10 +50,6 @@ template <Json_Type Json>
Http_Response<Json> make_http_error(int status, std::string message) {
return Http_Response<Json>{status, make_http_result<Json>(status, std::move(message), json_object<Json>())};
}
enum class Resource_Lock_Scope {
local,
root
};
template <class Model>
struct Resource_Transaction {
using Prepare_Function = std::function<void(const Model&, const Request_Context&)>;
@@ -69,7 +65,7 @@ Resource_Transaction<Model> make_resource_transaction(Function&& function) {
result.commit = std::forward<Function>(function);
return result;
}
template <class T, Json_Type Json, Basic_Lock Lock = std::shared_mutex>
template <class T, Json_Type Json, Basic_Lock Lock = std::mutex>
requires Writable_Adapted_Object<T> && std::copy_constructible<Object_Model_Type<T>>
class Resource_Service {
public:
@@ -88,8 +84,8 @@ public:
configure_raw_access(value, *owned_lock_);
}
}
template <Basic_Lock Field_Lock>
explicit Resource_Service(Synchronized_Value<Object, Lock, Field_Lock>& value, std::string path, Transaction transaction = {}) : path_(std::move(path)), transaction_(std::move(transaction)) {
template <structive::Synchronization_Policy Policy>
explicit Resource_Service(Managed_Value<Object, Policy>& value, std::string path, Transaction transaction = {}) : path_(std::move(path)), transaction_(std::move(transaction)) {
read_access_ = [&value](const Read_Function& function) {
value.read([&](const Object& object) {
function(object);
@@ -101,20 +97,16 @@ public:
});
};
}
template <class Root, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && std::same_as<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type, Object>
explicit Resource_Service(Synchronized_Field<Root, Lock, Field_Lock, Members...> value, std::string path, Transaction transaction = {}, Resource_Lock_Scope lock_scope = Resource_Lock_Scope::local) : path_(std::move(path)), transaction_(std::move(transaction)) {
if(lock_scope == Resource_Lock_Scope::root) {
configure_raw_access(detail::Synchronized_Access::value(value), detail::Synchronized_Access::mutex(value));
return;
}
template <class Managed, std::size_t Root_Index, auto... Members>
requires std::same_as<typename Managed_Field<Managed, Root_Index, Members...>::value_type, Object> && (!std::is_const_v<Managed>)
explicit Resource_Service(Managed_Field<Managed, Root_Index, Members...> value, std::string path, Transaction transaction = {}) : path_(std::move(path)), transaction_(std::move(transaction)) {
read_access_ = [value](const Read_Function& function) {
value.object_read([&](const Object& object) {
value.read([&](const Object& object) {
function(object);
});
};
write_access_ = [value](const Write_Function& function) mutable {
value.object_write([&](Object& object) {
value.write([&](Object& object) {
function(object);
});
};
+69 -70
View File
@@ -1,7 +1,7 @@
#pragma once
#include "adminive/adapter.hpp"
#include "adminive/descriptor.hpp"
#include "adminive/synchronized.hpp"
#include "adminive/managed.hpp"
#include <cmath>
#include <concepts>
#include <cstdint>
@@ -103,10 +103,10 @@ template <Json_Type Json, class T>
requires Creatable_Adapted_Object<T>
Json to_descriptor_json_with_defaults();
template <Json_Type Json, class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Json to_json(const T& value);
template <Json_Type Json, class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Json to_frontend_json(const T& value);
template <Json_Type Json, Writable_Adapted_Object T>
Update_Result assign_json(T& target, const Json& value);
@@ -413,14 +413,9 @@ Json model_descriptor_json(const Model* defaults = nullptr) {
json_set(field_json, "readable", item.is_readable());
json_set(field_json, "sensitive", item.is_sensitive());
json_set(field_json, "editable", item.is_editable());
const char* lock_mode = "automatic";
if(item.lock_mode() == Field_Lock_Mode::enabled) {
lock_mode = "enabled";
} else if(item.lock_mode() == Field_Lock_Mode::disabled) {
lock_mode = "disabled";
}
json_set(field_json, "lock_mode", lock_mode);
json_set(field_json, "synchronized", descriptor_field_requires_lock(item));
using Field = std::remove_cvref_t<decltype(item)>;
json_set(field_json, "writable", Field::managed_writable);
json_set(field_json, "synchronized", Field::managed_writable && Field::synchronized);
json_set(field_json, "creatable", item.is_creatable());
json_set(field_json, "visible", item.is_visible());
json_set(field_json, "required", item.is_required());
@@ -467,7 +462,7 @@ Json model_descriptor_json(const Model* defaults = nullptr) {
json_set(list, "column_reorderable", options.column_reorderable_);
Json result = json_object<Json>();
json_set(result, "protocol", "adminive.resource");
json_set(result, "protocol_version", 3);
json_set(result, "protocol_version", 4);
json_set(result, "name", descriptor.name());
json_set(result, "label", descriptor.label());
json_set(result, "value_type", "object");
@@ -506,7 +501,7 @@ Json model_to_json(const T& value, bool frontend) {
return result;
}
template <Json_Type Json, class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Json to_json(const T& value) {
using Object = std::remove_cvref_t<T>;
if constexpr(std::same_as<Object, Object_Model_Type<Object>>) {
@@ -516,7 +511,7 @@ Json to_json(const T& value) {
}
}
template <Json_Type Json, class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Json to_frontend_json(const T& value) {
using Object = std::remove_cvref_t<T>;
if constexpr(std::same_as<Object, Object_Model_Type<Object>>) {
@@ -526,44 +521,48 @@ Json to_frontend_json(const T& value) {
}
}
template <Json_Type Json, class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Json to_data_json(const T& value) {
return to_frontend_json<Json>(value);
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
Json to_json(const Synchronized_Value<T, Lock, Field_Lock>& value) {
return value.read([](const T& item) {
template <Json_Type Json, Managed_Value_Type T>
Json to_json(const T& value) {
return value.read([](const auto& item) {
return adminive::to_json<Json>(item);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
Json to_frontend_json(const Synchronized_Value<T, Lock, Field_Lock>& value) {
return value.read([](const T& item) {
template <Json_Type Json, Managed_Value_Type T>
Json to_frontend_json(const T& value) {
return value.read([](const auto& item) {
return adminive::to_frontend_json<Json>(item);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
Json to_data_json(const Synchronized_Value<T, Lock, Field_Lock>& value) {
template <Json_Type Json, Managed_Value_Type T>
Json to_data_json(const T& value) {
return to_frontend_json<Json>(value);
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
Json to_json(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& value) {
return value.object_read([](const auto& item) {
template <Json_Type Json, Managed_Field_Type T>
Json to_json(const T& value) {
return value.read([](const auto& item) {
return adminive::to_json<Json>(item);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
Json to_frontend_json(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& value) {
return value.object_read([](const auto& item) {
template <Json_Type Json, Managed_Field_Type T>
Json to_frontend_json(const T& value) {
return value.read([](const auto& item) {
return adminive::to_frontend_json<Json>(item);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
Json to_data_json(const Synchronized_Field<Root, Lock, Field_Lock, Members...>& value) {
template <Json_Type Json, Managed_Field_Type T>
Json to_data_json(const T& value) {
return to_frontend_json<Json>(value);
}
template <class Field>
bool field_writable(const Field& field, Write_Mode mode) {
using Descriptor = std::remove_cvref_t<Field>;
if constexpr(!Descriptor::managed_writable) {
return false;
}
if(mode == Write_Mode::internal) {
return true;
}
@@ -733,70 +732,70 @@ template <Json_Type Json, Writable_Adapted_Object T>
Update_Result apply_patch(T& target, const Json& patch) {
return apply_frontend_patch<Json>(target, patch);
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
requires Writable_Adapted_Object<T>
Update_Result assign_json(Synchronized_Value<T, Lock, Field_Lock>& target, const Json& value) {
return target.write([&](T& item) {
template <Json_Type Json, Managed_Value_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result assign_json(T& target, const Json& value) {
return target.write([&](auto& item) {
return adminive::assign_json<Json>(item, value);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
requires Writable_Adapted_Object<T>
Update_Result apply_json_patch(Synchronized_Value<T, Lock, Field_Lock>& target, const Json& patch) {
return target.write([&](T& item) {
template <Json_Type Json, Managed_Value_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_json_patch(T& target, const Json& patch) {
return target.write([&](auto& item) {
return adminive::apply_json_patch<Json>(item, patch);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
requires Creatable_Writable_Object<T>
Update_Result apply_frontend_create(Synchronized_Value<T, Lock, Field_Lock>& target, const Json& value) {
return target.write([&](T& item) {
template <Json_Type Json, Managed_Value_Type T>
requires Creatable_Writable_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_frontend_create(T& target, const Json& value) {
return target.write([&](auto& item) {
return adminive::apply_frontend_create<Json>(item, value);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
requires Writable_Adapted_Object<T>
Update_Result apply_frontend_patch(Synchronized_Value<T, Lock, Field_Lock>& target, const Json& patch) {
return target.write([&](T& item) {
template <Json_Type Json, Managed_Value_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_frontend_patch(T& target, const Json& patch) {
return target.write([&](auto& item) {
return adminive::apply_frontend_patch<Json>(item, patch);
});
}
template <Json_Type Json, class T, Basic_Lock Lock, Basic_Lock Field_Lock>
requires Writable_Adapted_Object<T>
Update_Result apply_patch(Synchronized_Value<T, Lock, Field_Lock>& target, const Json& patch) {
template <Json_Type Json, Managed_Value_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_patch(T& target, const Json& patch) {
return apply_frontend_patch<Json>(target, patch);
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && Writable_Adapted_Object<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type>
Update_Result assign_json(Synchronized_Field<Root, Lock, Field_Lock, Members...> target, const Json& value) {
return target.object_write([&](auto& item) {
template <Json_Type Json, Managed_Field_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result assign_json(T target, const Json& value) {
return target.write([&](auto& item) {
return adminive::assign_json<Json>(item, value);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && Writable_Adapted_Object<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type>
Update_Result apply_json_patch(Synchronized_Field<Root, Lock, Field_Lock, Members...> target, const Json& patch) {
return target.object_write([&](auto& item) {
template <Json_Type Json, Managed_Field_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_json_patch(T target, const Json& patch) {
return target.write([&](auto& item) {
return adminive::apply_json_patch<Json>(item, patch);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && Creatable_Writable_Object<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type>
Update_Result apply_frontend_create(Synchronized_Field<Root, Lock, Field_Lock, Members...> target, const Json& value) {
return target.object_write([&](auto& item) {
template <Json_Type Json, Managed_Field_Type T>
requires Creatable_Writable_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_frontend_create(T target, const Json& value) {
return target.write([&](auto& item) {
return adminive::apply_frontend_create<Json>(item, value);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && Writable_Adapted_Object<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type>
Update_Result apply_frontend_patch(Synchronized_Field<Root, Lock, Field_Lock, Members...> target, const Json& patch) {
return target.object_write([&](auto& item) {
template <Json_Type Json, Managed_Field_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_frontend_patch(T target, const Json& patch) {
return target.write([&](auto& item) {
return adminive::apply_frontend_patch<Json>(item, patch);
});
}
template <Json_Type Json, class Root, Basic_Lock Lock, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && Writable_Adapted_Object<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type>
Update_Result apply_patch(Synchronized_Field<Root, Lock, Field_Lock, Members...> target, const Json& patch) {
template <Json_Type Json, Managed_Field_Type T>
requires Writable_Adapted_Object<typename std::remove_cvref_t<T>::value_type>
Update_Result apply_patch(T target, const Json& patch) {
return apply_frontend_patch<Json>(target, patch);
}
template <Json_Type Json, Writable_Adapted_Object T>
@@ -810,7 +809,7 @@ T from_json(const Json& value) {
return result;
}
template <class T>
requires Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>
requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot<T>) && (!Managed_Value_Type<T>) && (!Managed_Field_Type<T>)
Update_Result validate(const T& value) {
Update_Result result;
try {
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <concepts>
namespace adminive {
struct No_Lock {
void lock() noexcept {}
bool try_lock() noexcept {
return true;
}
void unlock() noexcept {}
};
template <class T>
concept Basic_Lock = std::default_initializable<T> && requires(T& value) {
value.lock();
value.unlock();
};
}
@@ -0,0 +1,400 @@
#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 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 {};
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;
const value_type& read(const object_type& object) const noexcept(noexcept(Field::accessor_type::get(object.unsafe_value()))) {
return Field::accessor_type::get(object.unsafe_value());
}
value_type& read(object_type& object) const noexcept(noexcept(Field::accessor_type::get(object.unsafe_value()))) {
return Field::accessor_type::get(object.unsafe_value());
}
template <class Value>
void write(object_type& object, Value&& value) const requires writable && std::assignable_from<value_type&, Value> {
Field::accessor_type::get(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 = Field::accessor_type::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 = Field::accessor_type::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&>> {
const auto& keys = detail::managed_key_array<T>();
auto guard = this->lock_shared(std::span<const std::string_view>{keys});
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 {
std::optional<std::remove_cvref_t<Result>> result;
this->with_all_writable_locked([&](auto&) {
result.emplace(std::invoke(std::forward<Function>(function), value_));
});
return 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&>> {
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_shared({key});
const auto& value = detail::managed_path_value<Root, Root_Index, Members...>(owner_->unsafe_value());
return std::invoke(std::forward<Function>(function), value);
} else {
const auto& value = detail::managed_path_value<Root, Root_Index, Members...>(owner_->unsafe_value());
return std::invoke(std::forward<Function>(function), 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;
}
+1 -1
View File
@@ -285,7 +285,7 @@ int main() {
const Json encoded = adminive::to_json<Json>(config);
assert((adminive::json_get<Json, int>(adminive::Json_Adapter<Json>::at(encoded, "worker_count")) == 12));
assert((adminive::json_get<Json, std::string>(adminive::Json_Adapter<Json>::at(encoded, "mode")) == "secondary"));
adminive::Collection_Service<Config, Json, adminive::Empty_Lock> collection("/configs", {Config{}});
adminive::Collection_Service<Config, Json, adminive::No_Lock> collection("/configs", {Config{}});
const auto list = collection.list_response();
assert(list.status == 200);
const auto& data = adminive::Json_Adapter<Json>::at(list.body, "data");
+4 -4
View File
@@ -65,9 +65,9 @@ if(BUILD_TESTING)
target_include_directories(Adminive_Config_Store_Test PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src")
target_link_libraries(Adminive_Config_Store_Test PRIVATE Adminive_Example)
add_test(NAME Adminive_Config_Store_Test COMMAND Adminive_Config_Store_Test)
add_executable(Adminive_Synchronized_Test "${CMAKE_CURRENT_LIST_DIR}/tests/synchronized_test.cpp")
target_link_libraries(Adminive_Synchronized_Test PRIVATE Adminive::Default)
add_test(NAME Adminive_Synchronized_Test COMMAND Adminive_Synchronized_Test)
add_executable(Adminive_Managed_Test "${CMAKE_CURRENT_LIST_DIR}/tests/managed_test.cpp")
target_link_libraries(Adminive_Managed_Test PRIVATE Adminive::Default)
add_test(NAME Adminive_Managed_Test COMMAND Adminive_Managed_Test)
add_executable(Adminive_Drogon_Adapter_Test "${CMAKE_CURRENT_LIST_DIR}/tests/drogon_adapter_test.cpp")
target_include_directories(Adminive_Drogon_Adapter_Test BEFORE PRIVATE "${CMAKE_CURRENT_LIST_DIR}/tests/fake_drogon")
target_link_libraries(Adminive_Drogon_Adapter_Test PRIVATE Adminive::Default)
@@ -78,7 +78,7 @@ if(BUILD_TESTING)
target_compile_options(Adminive_Drogon_Adapter_Test PRIVATE /permissive-)
target_compile_options(Adminive_Safety_Test PRIVATE /permissive-)
target_compile_options(Adminive_Config_Store_Test PRIVATE /permissive-)
target_compile_options(Adminive_Synchronized_Test PRIVATE /permissive-)
target_compile_options(Adminive_Managed_Test PRIVATE /permissive-)
endif()
endif()
install(TARGETS Adminive_Service Adminive_Nlohmann Adminive_MagicEnum Adminive_BoostPfr Adminive_Httplib Adminive_Default EXPORT AdminiveServiceTargets)
@@ -82,11 +82,11 @@ public:
using Service = Resource_Service<T, Json, Lock>;
using Transaction = typename Service::Transaction;
Drogon_Resource(T& value, std::string path, Transaction transaction = {}, Lock* shared_lock = nullptr) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction), shared_lock)) {}
template <Basic_Lock Field_Lock>
explicit Drogon_Resource(Synchronized_Value<std::remove_cvref_t<T>, Lock, Field_Lock>& value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
template <class Root, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && std::same_as<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type, std::remove_cvref_t<T>>
explicit Drogon_Resource(Synchronized_Field<Root, Lock, Field_Lock, Members...> value, std::string path, Transaction transaction = {}, Resource_Lock_Scope lock_scope = Resource_Lock_Scope::local) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction), lock_scope)) {}
template <structive::Synchronization_Policy Policy>
explicit Drogon_Resource(Managed_Value<std::remove_cvref_t<T>, Policy>& value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
template <class Managed, std::size_t Root_Index, auto... Members>
requires (!std::is_const_v<Managed>) && std::same_as<typename Managed_Field<Managed, Root_Index, Members...>::value_type, std::remove_cvref_t<T>>
explicit Drogon_Resource(Managed_Field<Managed, Root_Index, Members...> value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
Json amis_schema() const {
return service_->amis_schema();
}
@@ -36,11 +36,11 @@ public:
using Service = Resource_Service<T, Json, Lock>;
using Transaction = typename Service::Transaction;
Http_Resource(T& value, std::string path, Transaction transaction = {}, Lock* shared_lock = nullptr) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction), shared_lock)) {}
template <Basic_Lock Field_Lock>
explicit Http_Resource(Synchronized_Value<std::remove_cvref_t<T>, Lock, Field_Lock>& value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
template <class Root, Basic_Lock Field_Lock, auto... Members>
requires (!std::is_const_v<Root>) && std::same_as<typename Synchronized_Field<Root, Lock, Field_Lock, Members...>::value_type, std::remove_cvref_t<T>>
explicit Http_Resource(Synchronized_Field<Root, Lock, Field_Lock, Members...> value, std::string path, Transaction transaction = {}, Resource_Lock_Scope lock_scope = Resource_Lock_Scope::local) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction), lock_scope)) {}
template <structive::Synchronization_Policy Policy>
explicit Http_Resource(Managed_Value<std::remove_cvref_t<T>, Policy>& value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
template <class Managed, std::size_t Root_Index, auto... Members>
requires (!std::is_const_v<Managed>) && std::same_as<typename Managed_Field<Managed, Root_Index, Members...>::value_type, std::remove_cvref_t<T>>
explicit Http_Resource(Managed_Field<Managed, Root_Index, Members...> value, std::string path, Transaction transaction = {}) : service_(std::make_shared<Service>(value, std::move(path), std::move(transaction))) {}
Json amis_schema() const {
return service_->amis_schema();
}
+29 -34
View File
@@ -31,8 +31,11 @@ void replace_file(const std::filesystem::path& temporary, const std::filesystem:
}
#endif
}
structive::Property_Synchronization shared_config_synchronization() {
return structive::property_synchronization(structive::Synchronization_Plan{structive::Synchronization_Default::shared});
}
Config_Store::Config_Store(std::filesystem::path path) : path_(std::move(path)) {
}
Config_Store::Config_Store(std::filesystem::path path) : path_(std::move(path)), data_(shared_config_synchronization()) {
load_or_create();
}
Config_Store::Radio_Service_Access Config_Store::radio_service() noexcept {
@@ -79,48 +82,48 @@ Resource_Transaction<Network_Table_Config> Config_Store::network_table_transacti
});
}
void Config_Store::persist_radio_service(const Radio_Service_Config& value) {
data_.write([&](Application_Config& data) {
Application_Config candidate = data;
data_.member<&Application_Config::radio_service>().write([&](Radio_Service_Config& target) {
Application_Config candidate = data_.unsafe_value();
candidate.radio_service = value;
persist_candidate(candidate);
data = std::move(candidate);
target = value;
});
}
void Config_Store::persist_alerts(const Alert_Config& value) {
data_.write([&](Application_Config& data) {
Application_Config candidate = data;
data_.member<&Application_Config::alerts>().write([&](Alert_Config& target) {
Application_Config candidate = data_.unsafe_value();
candidate.alerts = value;
persist_candidate(candidate);
data = std::move(candidate);
target = value;
});
}
void Config_Store::persist_serial_table(const Serial_Table_Config& value) {
data_.write([&](Application_Config& data) {
Application_Config candidate = data;
data_.member<&Application_Config::serial_table>().write([&](Serial_Table_Config& target) {
Application_Config candidate = data_.unsafe_value();
candidate.serial_table = value;
persist_candidate(candidate);
data = std::move(candidate);
target = value;
});
}
void Config_Store::persist_network_table(const Network_Table_Config& value) {
data_.write([&](Application_Config& data) {
Application_Config candidate = data;
data_.member<&Application_Config::network_table>().write([&](Network_Table_Config& target) {
Application_Config candidate = data_.unsafe_value();
candidate.network_table = value;
persist_candidate(candidate);
data = std::move(candidate);
target = value;
});
}
void Config_Store::persist_default_device_type(Device_Table_Type value) {
data_.write([&](Application_Config& data) {
Application_Config candidate = data;
data_.member<&Application_Config::default_device_type>().write([&](Device_Table_Type& target) {
Application_Config candidate = data_.unsafe_value();
candidate.default_device_type = value;
persist_candidate(candidate);
data = std::move(candidate);
target = value;
});
}
void Config_Store::load_or_create() {
if(!std::filesystem::exists(path_)) {
persist_candidate(detail::Synchronized_Access::value(data_));
persist_candidate(data_.unsafe_value());
return;
}
std::ifstream input(path_);
@@ -133,7 +136,7 @@ void Config_Store::load_or_create() {
if(!result.success) {
throw std::runtime_error("invalid configuration file: " + result.message);
}
detail::Synchronized_Access::value(data_) = std::move(candidate);
data_.unsafe_value() = std::move(candidate);
}
void Config_Store::persist_candidate(const Application_Config& value) {
const auto parent = path_.parent_path();
@@ -157,24 +160,16 @@ void Config_Store::persist_candidate(const Application_Config& value) {
}
replace_file(temporary, path_);
}
void Config_Store::persist_radio_service_locked(const Radio_Service_Config& value) {
Application_Config candidate = detail::Synchronized_Access::value(data_);
candidate.radio_service = value;
persist_candidate(candidate);
void Config_Store::persist_radio_service_locked(const Radio_Service_Config&) {
persist_candidate(data_.unsafe_value());
}
void Config_Store::persist_alerts_locked(const Alert_Config& value) {
Application_Config candidate = detail::Synchronized_Access::value(data_);
candidate.alerts = value;
persist_candidate(candidate);
void Config_Store::persist_alerts_locked(const Alert_Config&) {
persist_candidate(data_.unsafe_value());
}
void Config_Store::persist_serial_table_locked(const Serial_Table_Config& value) {
Application_Config candidate = detail::Synchronized_Access::value(data_);
candidate.serial_table = value;
persist_candidate(candidate);
void Config_Store::persist_serial_table_locked(const Serial_Table_Config&) {
persist_candidate(data_.unsafe_value());
}
void Config_Store::persist_network_table_locked(const Network_Table_Config& value) {
Application_Config candidate = detail::Synchronized_Access::value(data_);
candidate.network_table = value;
persist_candidate(candidate);
void Config_Store::persist_network_table_locked(const Network_Table_Config&) {
persist_candidate(data_.unsafe_value());
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
namespace adminive::example {
class Config_Store {
public:
using Storage = Synchronized_Value<Application_Config>;
using Storage = Managed_Value<Application_Config>;
using Radio_Service_Access = decltype(std::declval<Storage&>().member<&Application_Config::radio_service>());
using Alert_Access = decltype(std::declval<Storage&>().member<&Application_Config::alerts>());
using Serial_Table_Access = decltype(std::declval<Storage&>().member<&Application_Config::serial_table>());
+2 -2
View File
@@ -22,7 +22,7 @@ Json make_table_page(std::string_view title, std::string_view color, Json config
return Json{{"type", "container"}, {"body", Json::array({std::move(heading), std::move(config_form), std::move(table_page.at("body"))})}};
}
}
Serial_Device_Table::Serial_Device_Table(Config_Store& store) : store_(store), config_resource_(store.serial_table(), "/admin/device_tables/serial/config", store.serial_table_transaction(), Resource_Lock_Scope::root), rows_("/admin/device_tables/serial/items", make_serial_rows()) {}
Serial_Device_Table::Serial_Device_Table(Config_Store& store) : store_(store), config_resource_(store.serial_table(), "/admin/device_tables/serial/config", store.serial_table_transaction()), rows_("/admin/device_tables/serial/items", make_serial_rows()) {}
Device_Table_Type Serial_Device_Table::type() const noexcept {
return Device_Table_Type::serial;
}
@@ -40,7 +40,7 @@ void Serial_Device_Table::bind(httplib::Server& server) {
config_resource_.bind(server);
rows_.bind(server);
}
Network_Device_Table::Network_Device_Table(Config_Store& store) : store_(store), config_resource_(store.network_table(), "/admin/device_tables/network/config", store.network_table_transaction(), Resource_Lock_Scope::root), rows_("/admin/device_tables/network/items", make_network_rows()) {}
Network_Device_Table::Network_Device_Table(Config_Store& store) : store_(store), config_resource_(store.network_table(), "/admin/device_tables/network/config", store.network_table_transaction()), rows_("/admin/device_tables/network/items", make_network_rows()) {}
Device_Table_Type Network_Device_Table::type() const noexcept {
return Device_Table_Type::network;
}
+5 -5
View File
@@ -105,11 +105,11 @@ struct Type_Descriptor<example::Application_Config> {
using T = example::Application_Config;
return object<T>(
"application_config",
ADMINIVE_FIELD_LABEL(T, default_device_type, "默认设备类型").locked(),
ADMINIVE_FIELD_LABEL(T, radio_service, "无线电服务"),
ADMINIVE_FIELD_LABEL(T, alerts, "告警"),
ADMINIVE_FIELD_LABEL(T, serial_table, "串口表格"),
ADMINIVE_FIELD_LABEL(T, network_table, "网络表格")
ADMINIVE_FIELD_LABEL(T, default_device_type, "默认设备类型").read_write(),
ADMINIVE_FIELD_LABEL(T, radio_service, "无线电服务").read_write(),
ADMINIVE_FIELD_LABEL(T, alerts, "告警").read_write(),
ADMINIVE_FIELD_LABEL(T, serial_table, "串口表格").read_write(),
ADMINIVE_FIELD_LABEL(T, network_table, "网络表格").read_write()
).label("Adminive 配置");
}
};
+1 -1
View File
@@ -12,7 +12,7 @@ Radio_State make_radio_state(Radio_Mode mode, int port, int buffer_count, int lo
return result;
}
}
Server_App::Server_App(int port, std::filesystem::path frontend_dist, std::filesystem::path config_file) : port_(port), frontend_dist_(std::move(frontend_dist)), config_store_(std::move(config_file)), state_resource_("/admin/radio_states", make_radio_states()), service_config_resource_(config_store_.radio_service(), "/admin/config/radio", config_store_.radio_service_transaction(), Resource_Lock_Scope::root), alert_config_resource_(config_store_.alerts(), "/admin/config/alerts", config_store_.alerts_transaction(), Resource_Lock_Scope::root), device_tables_(config_store_) {
Server_App::Server_App(int port, std::filesystem::path frontend_dist, std::filesystem::path config_file) : port_(port), frontend_dist_(std::move(frontend_dist)), config_store_(std::move(config_file)), state_resource_("/admin/radio_states", make_radio_states()), service_config_resource_(config_store_.radio_service(), "/admin/config/radio", config_store_.radio_service_transaction()), alert_config_resource_(config_store_.alerts(), "/admin/config/alerts", config_store_.alerts_transaction()), device_tables_(config_store_) {
state_resource_.register_overview_status<Server_Status>("/admin/status", 2000);
state_resource_.register_status<&Radio_State::status>(2000);
bind_routes();
@@ -289,15 +289,15 @@ int main() {
const auto inherited = adminive::to_descriptor_json<Json, Inherited_Config>();
assert(inherited.at("fields").at(0).at("name") == "alpha");
assert(inherited.at("fields").at(1).at("name") == "beta");
adminive::Synchronized_Value<Inherited_Config> synchronized_inherited;
synchronized_inherited.field<0>().write([](int& value) {
adminive::Managed_Value<Inherited_Config> managed_inherited;
managed_inherited.field<0>().write([](int& value) {
value = 12;
});
synchronized_inherited.field<1>().write([](std::string& value) {
managed_inherited.field<1>().write([](std::string& value) {
value = "changed";
});
assert(synchronized_inherited.field<0>().snapshot() == 12);
assert(synchronized_inherited.field<1>().snapshot() == "changed");
assert(managed_inherited.field<0>().snapshot() == 12);
assert(managed_inherited.field<1>().snapshot() == "changed");
Control_Config controls;
const auto control_form = adminive::to_amis_form_schema<Json>(controls);
assert(control_form.at("body").at(0).at("type") == "input-color");
+190
View File
@@ -0,0 +1,190 @@
#include "adminive/adapters/nlohmann_json.hpp"
#include "adminive/http.hpp"
#include <atomic>
#include <cassert>
#include <string>
#include <thread>
namespace managed_test {
using Json = nlohmann::json;
struct Section_Config {
int editable_value{1};
int immutable_value{2};
int backend_value{3};
int unsynchronized_value{4};
};
struct Root_Config {
Section_Config section;
int immutable_value{5};
};
struct Concurrent_Config {
int first{};
int second{};
int immutable_value{7};
};
struct Probe_Lock {
static inline std::atomic<int> exclusive_locks{};
static inline std::atomic<int> shared_locks{};
void lock() noexcept {
exclusive_locks.fetch_add(1, std::memory_order_relaxed);
}
void unlock() noexcept {}
void lock_shared() noexcept {
shared_locks.fetch_add(1, std::memory_order_relaxed);
}
void unlock_shared() noexcept {}
static void reset() noexcept {
exclusive_locks.store(0, std::memory_order_relaxed);
shared_locks.store(0, std::memory_order_relaxed);
}
};
template <class Field>
concept Writable_Field = requires(Field field) {
field.write([](auto& value) {
++value;
});
};
template <class Field>
concept Reference_Escaping_Read = requires(Field field) {
field.read([](const auto& value) -> const auto& {
return value;
});
};
}
namespace adminive {
template <>
struct Type_Descriptor<managed_test::Section_Config> {
static auto get() {
using T = managed_test::Section_Config;
return object<T>("section", "Section", ADMINIVE_FIELD(T, editable_value).editable(), ADMINIVE_FIELD(T, immutable_value), ADMINIVE_FIELD(T, backend_value).read_write(), ADMINIVE_FIELD(T, unsynchronized_value).editable().unsynchronized());
}
};
template <>
struct Type_Descriptor<managed_test::Root_Config> {
static auto get() {
using T = managed_test::Root_Config;
return object<T>("root", "Root", ADMINIVE_FIELD(T, section), ADMINIVE_FIELD(T, immutable_value));
}
};
template <>
struct Type_Descriptor<managed_test::Concurrent_Config> {
static auto get() {
using T = managed_test::Concurrent_Config;
return object<T>("concurrent", "Concurrent", ADMINIVE_FIELD(T, first).editable(), ADMINIVE_FIELD(T, second).editable(), ADMINIVE_FIELD(T, immutable_value));
}
};
}
int main() {
using namespace managed_test;
using Guarded = adminive::Managed_Value<Section_Config, adminive::Mutex_Policy<Probe_Lock>>;
Guarded guarded;
auto immutable = guarded.member<&Section_Config::immutable_value>();
auto editable = guarded.member<&Section_Config::editable_value>();
auto backend = guarded.member<&Section_Config::backend_value>();
auto unsynchronized = guarded.member<&Section_Config::unsynchronized_value>();
static_assert(!Writable_Field<decltype(immutable)>);
static_assert(Writable_Field<decltype(editable)>);
static_assert(Writable_Field<decltype(backend)>);
static_assert(Writable_Field<decltype(unsynchronized)>);
static_assert(!Reference_Escaping_Read<decltype(immutable)>);
const auto resolved = guarded.resolved_synchronization();
assert(resolved.lock_count == 2);
assert(resolved.slot(0) != structive::Resolved_Synchronization_View::unsynchronized_slot);
assert(resolved.slot(1) == structive::Resolved_Synchronization_View::unsynchronized_slot);
assert(resolved.slot(2) != structive::Resolved_Synchronization_View::unsynchronized_slot);
assert(resolved.slot(0) != resolved.slot(2));
assert(resolved.slot(3) == structive::Resolved_Synchronization_View::unsynchronized_slot);
Probe_Lock::reset();
assert(immutable.snapshot() == 2);
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 0);
assert(Probe_Lock::shared_locks.load(std::memory_order_relaxed) == 0);
Probe_Lock::reset();
editable.write([](int& value) {
value = 11;
});
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 1);
assert(Probe_Lock::shared_locks.load(std::memory_order_relaxed) == 0);
Probe_Lock::reset();
backend.write([](int& value) {
value = 12;
});
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 1);
Probe_Lock::reset();
unsynchronized.write([](int& value) {
value = 13;
});
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 0);
assert(Probe_Lock::shared_locks.load(std::memory_order_relaxed) == 0);
const auto descriptor = adminive::to_descriptor_json<Json, Section_Config>();
assert(descriptor.at("protocol_version") == 4);
assert(!descriptor.at("fields").at(0).contains("lock_mode"));
assert(descriptor.at("fields").at(0).at("writable") == true);
assert(descriptor.at("fields").at(0).at("synchronized") == true);
assert(descriptor.at("fields").at(1).at("writable") == false);
assert(descriptor.at("fields").at(1).at("synchronized") == false);
assert(descriptor.at("fields").at(2).at("writable") == true);
assert(descriptor.at("fields").at(2).at("editable") == false);
assert(descriptor.at("fields").at(2).at("synchronized") == true);
assert(descriptor.at("fields").at(3).at("writable") == true);
assert(descriptor.at("fields").at(3).at("synchronized") == false);
adminive::Managed_Value<Root_Config, adminive::Mutex_Policy<Probe_Lock>> nested;
auto section = nested.member<&Root_Config::section>();
static_assert(!Writable_Field<decltype(section)>);
Probe_Lock::reset();
assert(section.member<&Section_Config::immutable_value>().snapshot() == 2);
assert(Probe_Lock::shared_locks.load(std::memory_order_relaxed) == 0);
Probe_Lock::reset();
section.member<&Section_Config::editable_value>().write([](int& value) {
value = 31;
});
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 1);
Probe_Lock::reset();
section.member<&Section_Config::unsynchronized_value>().write([](int& value) {
value = 32;
});
assert(Probe_Lock::exclusive_locks.load(std::memory_order_relaxed) == 0);
adminive::Managed_Value<Section_Config, structive::No_Lock_Policy> no_lock;
adminive::Resource_Service<Section_Config, Json> service(no_lock, "/section");
const auto rejected = service.update_response(R"({"backend_value":99,"immutable_value":88})");
assert(rejected.status == 422);
const auto update = service.update_response(R"({"editable_value":21,"unsynchronized_value":22})");
assert(update.status == 200);
const auto no_lock_snapshot = no_lock.snapshot();
assert(no_lock_snapshot.editable_value == 21);
assert(no_lock_snapshot.backend_value == 3);
assert(no_lock_snapshot.immutable_value == 2);
assert(no_lock_snapshot.unsynchronized_value == 22);
adminive::Managed_Value<Concurrent_Config> concurrent;
std::atomic<bool> start{};
auto first_writer = std::thread([&] {
while(!start.load(std::memory_order_acquire)) {}
for(int index = 0; index < 5000; ++index) {
concurrent.member<&Concurrent_Config::first>().write([index](int& value) {
value = index;
});
}
});
auto second_writer = std::thread([&] {
while(!start.load(std::memory_order_acquire)) {}
for(int index = 0; index < 5000; ++index) {
concurrent.member<&Concurrent_Config::second>().write([index](int& value) {
value = index;
});
}
});
auto serializer = std::thread([&] {
while(!start.load(std::memory_order_acquire)) {}
for(int index = 0; index < 1000; ++index) {
const Json value = adminive::to_json<Json>(concurrent);
assert(value.at("immutable_value") == 7);
}
});
start.store(true, std::memory_order_release);
first_writer.join();
second_writer.join();
serializer.join();
const auto concurrent_snapshot = concurrent.snapshot();
assert(concurrent_snapshot.first == 4999);
assert(concurrent_snapshot.second == 4999);
assert(concurrent_snapshot.immutable_value == 7);
return 0;
}