更完
This commit is contained in:
@@ -263,6 +263,28 @@ struct adminive::Object_Adapter<Runtime_Config> {
|
||||
};
|
||||
```
|
||||
|
||||
如果运行时对象已经拥有权威状态和并发策略,不要为了 Adminive 再造一份完整配置快照。`field(name, label, accessor)` 直接接受 Structive `Property_Accessor`;默认数据成员仍使用 `structive::Member_Accessor`,方法型或子控件属性可使用 Structive 的 getter/setter accessor:
|
||||
|
||||
```cpp
|
||||
template <>
|
||||
struct adminive::Type_Descriptor<Runtime_Control> {
|
||||
static auto get() {
|
||||
auto accessor = structive::trusted_callable_accessor<Runtime_Control>(
|
||||
[](const Runtime_Control& value) { return value.setting(); },
|
||||
[](Runtime_Control& value, int setting) {
|
||||
value.apply_setting(setting);
|
||||
});
|
||||
return adminive::object<Runtime_Control>(
|
||||
"runtime_control",
|
||||
adminive::field("setting", "Setting", std::move(accessor))
|
||||
.editable()
|
||||
.unsynchronized());
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
这种直接描述不要求对象可复制,也不经过 `Object_Adapter`。`.unsynchronized()` 只表示 Adminive/Structive 不增加同步域;访问器调用的业务方法及对象自身同步仍是唯一权威实现。
|
||||
|
||||
`Resource_Service` 始终通过 `Object_Adapter<T>::commit()`提交运行时模型。外部持久化和系统副作用通过 `Resource_Transaction<Model>` 分成准备、提交和回滚三个阶段:
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "adminive/adapter.hpp"
|
||||
#include "adminive/concepts.hpp"
|
||||
#include "adminive/presentation.hpp"
|
||||
#include <structive/property/accessor.hpp>
|
||||
#include <cctype>
|
||||
#include <concepts>
|
||||
#include <map>
|
||||
@@ -23,43 +24,33 @@ template <class T>
|
||||
concept Reflected_Type = requires {
|
||||
{ Reflection_Adapter<std::remove_cvref_t<T>>::field_count } -> std::convertible_to<std::size_t>;
|
||||
};
|
||||
template <auto Member>
|
||||
struct Member_Pointer_Traits;
|
||||
template <class Owner, class Member_Type, Member_Type Owner::*Member>
|
||||
struct Member_Pointer_Traits<Member> {
|
||||
using owner_type = Owner;
|
||||
using member_type = Member_Type;
|
||||
static constexpr auto value = Member;
|
||||
};
|
||||
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;
|
||||
}
|
||||
template <class Object>
|
||||
static decltype(auto) get(const Object& object) noexcept {
|
||||
return object.*Member;
|
||||
}
|
||||
};
|
||||
template <class T, std::size_t Index>
|
||||
struct Reflected_Field_Accessor {
|
||||
using owner_type = T;
|
||||
using object_type = T;
|
||||
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>;
|
||||
using value_type = std::remove_cvref_t<reference_type>;
|
||||
struct storage_identity {};
|
||||
using dependency_spec = structive::No_Property_Dependencies;
|
||||
static constexpr bool readable = true;
|
||||
static constexpr bool writable =
|
||||
!std::is_const_v<std::remove_reference_t<reference_type>>;
|
||||
static constexpr bool synchronized_view_read = false;
|
||||
static constexpr bool trusted_object_access = true;
|
||||
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))) {
|
||||
decltype(auto) read(T& object) const noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
|
||||
return Reflection_Adapter<T>::template get<Index>(object);
|
||||
}
|
||||
static decltype(auto) get(const T& object) noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
|
||||
decltype(auto) read(const T& object) const noexcept(noexcept(Reflection_Adapter<T>::template get<Index>(object))) {
|
||||
return Reflection_Adapter<T>::template get<Index>(object);
|
||||
}
|
||||
template <class Value>
|
||||
void write(T& object, Value&& value) const
|
||||
requires std::assignable_from<value_type&, Value> {
|
||||
Reflection_Adapter<T>::template get<Index>(object) = std::forward<Value>(value);
|
||||
}
|
||||
};
|
||||
inline bool valid_data_name(std::string_view name) noexcept {
|
||||
if(name.empty()) {
|
||||
@@ -102,35 +93,39 @@ struct Field_Metadata {
|
||||
bool include_default{true};
|
||||
bool required{};
|
||||
};
|
||||
template <class Accessor, bool Managed_Writable = false, bool Synchronized = true>
|
||||
template <structive::Property_Accessor 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;
|
||||
using owner_type = typename Accessor::object_type;
|
||||
using member_type = typename Accessor::value_type;
|
||||
static constexpr bool managed_writable = Managed_Writable;
|
||||
static constexpr bool synchronized = Synchronized;
|
||||
explicit Field_Descriptor(std::string name) {
|
||||
Field_Descriptor(std::string name, Accessor accessor = {})
|
||||
: accessor_(std::move(accessor)) {
|
||||
metadata_.name = std::move(name);
|
||||
metadata_.presentation.label = make_label(metadata_.name);
|
||||
}
|
||||
Field_Descriptor(std::string name, std::string label) {
|
||||
Field_Descriptor(std::string name, std::string label, Accessor accessor = {})
|
||||
: accessor_(std::move(accessor)) {
|
||||
metadata_.name = std::move(name);
|
||||
metadata_.presentation.label = std::move(label);
|
||||
}
|
||||
explicit Field_Descriptor(Field_Metadata metadata) : metadata_(std::move(metadata)) {}
|
||||
auto editable() const {
|
||||
Field_Descriptor(Field_Metadata metadata, Accessor accessor)
|
||||
: accessor_(std::move(accessor)), metadata_(std::move(metadata)) {}
|
||||
auto editable() const requires Accessor::writable {
|
||||
auto result = rebind<true, Synchronized>();
|
||||
result.metadata_.editable = true;
|
||||
return result;
|
||||
}
|
||||
auto creatable() const {
|
||||
auto creatable() const requires Accessor::writable {
|
||||
auto result = rebind<true, Synchronized>();
|
||||
result.metadata_.creatable = true;
|
||||
return result;
|
||||
}
|
||||
auto read_write() const {
|
||||
auto read_write() const requires Accessor::writable {
|
||||
return rebind<true, Synchronized>();
|
||||
}
|
||||
auto unsynchronized() const {
|
||||
@@ -219,13 +214,24 @@ public:
|
||||
}
|
||||
template <class Object>
|
||||
requires std::convertible_to<std::remove_reference_t<Object>*, owner_type*>
|
||||
decltype(auto) get(Object& object) const noexcept(noexcept(Accessor::get(object))) {
|
||||
return Accessor::get(object);
|
||||
decltype(auto) get(Object& object) const
|
||||
noexcept(noexcept(accessor_.read(object))) {
|
||||
return accessor_.read(object);
|
||||
}
|
||||
template <class Object>
|
||||
requires std::convertible_to<const std::remove_reference_t<Object>*, const owner_type*>
|
||||
decltype(auto) get(const Object& object) const noexcept(noexcept(Accessor::get(object))) {
|
||||
return Accessor::get(object);
|
||||
decltype(auto) get(const Object& object) const
|
||||
noexcept(noexcept(accessor_.read(object))) {
|
||||
return accessor_.read(object);
|
||||
}
|
||||
template <class Object, class Value>
|
||||
requires std::convertible_to<std::remove_reference_t<Object>*, owner_type*> &&
|
||||
requires(const Accessor& accessor, Object& object, Value&& value) {
|
||||
accessor.write(object, std::forward<Value>(value));
|
||||
}
|
||||
void set(Object& object, Value&& value) const
|
||||
noexcept(noexcept(accessor_.write(object, std::forward<Value>(value)))) {
|
||||
accessor_.write(object, std::forward<Value>(value));
|
||||
}
|
||||
const std::string& name() const noexcept {
|
||||
return metadata_.name;
|
||||
@@ -269,10 +275,11 @@ public:
|
||||
private:
|
||||
template <bool New_Writable, bool New_Synchronized>
|
||||
auto rebind() const {
|
||||
return Field_Descriptor<Accessor, New_Writable, New_Synchronized>(metadata_);
|
||||
return Field_Descriptor<Accessor, New_Writable, New_Synchronized>(metadata_, accessor_);
|
||||
}
|
||||
template <class, bool, bool>
|
||||
template <structive::Property_Accessor, bool, bool>
|
||||
friend class Field_Descriptor;
|
||||
[[no_unique_address]] Accessor accessor_;
|
||||
Field_Metadata metadata_;
|
||||
};
|
||||
template <class T>
|
||||
@@ -286,11 +293,19 @@ concept Field_Descriptor_Type = requires {
|
||||
};
|
||||
template <auto Member>
|
||||
auto field(std::string name) {
|
||||
return Field_Descriptor<Member_Field_Accessor<Member>>(std::move(name));
|
||||
return Field_Descriptor<structive::Member_Accessor<Member>>(std::move(name));
|
||||
}
|
||||
template <auto Member>
|
||||
auto field(std::string name, std::string label) {
|
||||
return Field_Descriptor<Member_Field_Accessor<Member>>(std::move(name), std::move(label));
|
||||
return Field_Descriptor<structive::Member_Accessor<Member>>(std::move(name), std::move(label));
|
||||
}
|
||||
template <structive::Property_Accessor Accessor>
|
||||
auto field(std::string name, Accessor accessor) {
|
||||
return Field_Descriptor<Accessor>(std::move(name), std::move(accessor));
|
||||
}
|
||||
template <structive::Property_Accessor Accessor>
|
||||
auto field(std::string name, std::string label, Accessor accessor) {
|
||||
return Field_Descriptor<Accessor>(std::move(name), std::move(label), std::move(accessor));
|
||||
}
|
||||
template <Reflected_Type T, std::size_t Index>
|
||||
auto reflected_field() {
|
||||
@@ -384,7 +399,7 @@ struct Identity_Field_Customizer {
|
||||
};
|
||||
template <Reflected_Type T, class Customizer, std::size_t... Indexes>
|
||||
auto reflected_object_with_impl(std::string name, std::string label, Customizer customizer, std::index_sequence<Indexes...>) {
|
||||
return object<T>(std::move(name), std::move(label), customizer.template operator()<Indexes>(reflected_field<T, Indexes>())...);
|
||||
return adminive::object<T>(std::move(name), std::move(label), customizer.template operator()<Indexes>(reflected_field<T, Indexes>())...);
|
||||
}
|
||||
template <Reflected_Type T, class Customizer>
|
||||
auto reflected_object_with(std::string name, Customizer customizer) {
|
||||
|
||||
@@ -100,6 +100,9 @@ template <class T>
|
||||
concept Writable_Adapted_Object = Snapshot_Adapted_Object<T> && Object_Adapter_With_Commit<T>;
|
||||
template <class T>
|
||||
concept Creatable_Writable_Object = Writable_Adapted_Object<T> && Object_Adapter_With_Create<T>;
|
||||
template <class T>
|
||||
concept Direct_Described_Object = Described_Type<std::remove_cvref_t<T>> &&
|
||||
(!Writable_Adapted_Object<T>);
|
||||
template <Json_Type Json, class T>
|
||||
requires Described_Type<Object_Model_Type<T>>
|
||||
Json to_descriptor_json();
|
||||
@@ -114,6 +117,8 @@ requires (Described_Type<std::remove_cvref_t<T>> || Object_Adapter_With_Snapshot
|
||||
Json to_frontend_json(const T& value);
|
||||
template <Json_Type Json, Writable_Adapted_Object T>
|
||||
Update_Result assign_json(T& target, const Json& value);
|
||||
template <Json_Type Json, Direct_Described_Object T>
|
||||
Update_Result assign_json(T& target, const Json& value);
|
||||
template <Json_Type Json, Writable_Adapted_Object T>
|
||||
Update_Result apply_object_json(T& target, const Json& value, Write_Mode mode, bool complete);
|
||||
template <Json_Type Json, class T>
|
||||
@@ -583,7 +588,20 @@ Update_Result apply_model_json(Model& candidate, const Json& value, Write_Mode m
|
||||
return;
|
||||
}
|
||||
try {
|
||||
assign_json_value<Json>(item.get(candidate), Json_Adapter<Json>::at(value, item.name()), Write_Context{mode, complete});
|
||||
using Field = std::remove_cvref_t<decltype(item)>;
|
||||
using Field_Value = typename Field::member_type;
|
||||
if constexpr(std::is_lvalue_reference_v<decltype(item.get(candidate))> &&
|
||||
!std::is_const_v<std::remove_reference_t<decltype(item.get(candidate))>>) {
|
||||
assign_json_value<Json>(item.get(candidate),
|
||||
Json_Adapter<Json>::at(value, item.name()),
|
||||
Write_Context{mode, complete});
|
||||
} else {
|
||||
Field_Value field_candidate(item.get(std::as_const(candidate)));
|
||||
assign_json_value<Json>(field_candidate,
|
||||
Json_Adapter<Json>::at(value, item.name()),
|
||||
Write_Context{mode, complete});
|
||||
item.set(candidate, std::move(field_candidate));
|
||||
}
|
||||
} catch(const Json_Assignment_Error& error) {
|
||||
merge_update_errors(result, error.result(), item.name());
|
||||
} catch(const Field_Validation_Error& error) {
|
||||
@@ -711,6 +729,22 @@ 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, Direct_Described_Object T>
|
||||
Update_Result assign_json(T& target, const Json& value) {
|
||||
return apply_model_json<Json>(target, value, Write_Mode::internal, true);
|
||||
}
|
||||
template <Json_Type Json, Direct_Described_Object T>
|
||||
Update_Result apply_json_patch(T& target, const Json& patch) {
|
||||
return apply_model_json<Json>(target, patch, Write_Mode::internal, false);
|
||||
}
|
||||
template <Json_Type Json, Direct_Described_Object T>
|
||||
Update_Result apply_frontend_patch(T& target, const Json& patch) {
|
||||
return apply_model_json<Json>(target, patch, Write_Mode::update, false);
|
||||
}
|
||||
template <Json_Type Json, Direct_Described_Object T>
|
||||
Update_Result apply_patch(T& target, const Json& patch) {
|
||||
return apply_frontend_patch<Json>(target, patch);
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -97,6 +97,11 @@ consteval bool field_requires_synchronization() {
|
||||
}
|
||||
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;
|
||||
@@ -107,15 +112,26 @@ struct Managed_Field_Accessor {
|
||||
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());
|
||||
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());
|
||||
}
|
||||
value_type& read(object_type& object) const noexcept(noexcept(Field::accessor_type::get(object.unsafe_value()))) {
|
||||
return Field::accessor_type::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 && std::assignable_from<value_type&, Value> {
|
||||
Field::accessor_type::get(object.unsafe_value()) = std::forward<Value>(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>
|
||||
@@ -227,7 +243,7 @@ decltype(auto) path_value(const Object& object) {
|
||||
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);
|
||||
auto&& root = managed_descriptor_field<T, Root_Index>().get(object);
|
||||
if constexpr(sizeof...(Members) == 0) {
|
||||
return std::forward<decltype(root)>(root);
|
||||
} else {
|
||||
@@ -237,7 +253,7 @@ decltype(auto) managed_path_value(T& object) {
|
||||
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);
|
||||
auto&& root = managed_descriptor_field<T, Root_Index>().get(object);
|
||||
if constexpr(sizeof...(Members) == 0) {
|
||||
return std::forward<decltype(root)>(root);
|
||||
} else {
|
||||
|
||||
@@ -68,7 +68,7 @@ inline View_Node view_field(std::string name) {
|
||||
}
|
||||
template <auto Member>
|
||||
std::string described_member_name() {
|
||||
using Owner = typename Member_Pointer_Traits<Member>::owner_type;
|
||||
using Owner = typename structive::Member_Pointer_Traits<decltype(Member)>::object_type;
|
||||
const auto descriptor = describe<Owner>();
|
||||
std::string result;
|
||||
std::apply([&](const auto&... item) {
|
||||
|
||||
@@ -34,6 +34,20 @@ struct Mode_Base {
|
||||
struct Config : Runtime_Base, Mode_Base {
|
||||
std::string name{"default"};
|
||||
};
|
||||
|
||||
class Runtime_Control final {
|
||||
public:
|
||||
Runtime_Control() = default;
|
||||
Runtime_Control(const Runtime_Control&) = delete;
|
||||
Runtime_Control& operator=(const Runtime_Control&) = delete;
|
||||
|
||||
[[nodiscard]] int setting() const noexcept { return setting_; }
|
||||
void apply_setting(int value) noexcept { setting_ = value; }
|
||||
|
||||
private:
|
||||
int setting_{3};
|
||||
};
|
||||
|
||||
}
|
||||
namespace adminive {
|
||||
template <>
|
||||
@@ -263,6 +277,21 @@ struct Type_Descriptor<adapter_test::Config> {
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct Type_Descriptor<adapter_test::Runtime_Control> {
|
||||
static auto get() {
|
||||
auto accessor = structive::trusted_callable_accessor<adapter_test::Runtime_Control>(
|
||||
[](const adapter_test::Runtime_Control& value) { return value.setting(); },
|
||||
[](adapter_test::Runtime_Control& target, int value) {
|
||||
target.apply_setting(value);
|
||||
});
|
||||
return adminive::object<adapter_test::Runtime_Control>(
|
||||
"runtime_control", "Runtime Control",
|
||||
adminive::field("setting", "Setting", std::move(accessor))
|
||||
.editable()
|
||||
.unsynchronized());
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct Type_View_Descriptor<adapter_test::Config> {
|
||||
static Table_View table() {
|
||||
using T = adapter_test::Config;
|
||||
@@ -295,6 +324,16 @@ int main() {
|
||||
const Json encoded = adminive::to_json<Json>(config);
|
||||
ADMINIVE_CHECK((adminive::json_get<Json, int>(adminive::Json_Adapter<Json>::at(encoded, "worker_count")) == 12));
|
||||
ADMINIVE_CHECK((adminive::json_get<Json, std::string>(adminive::Json_Adapter<Json>::at(encoded, "mode")) == "secondary"));
|
||||
adapter_test::Runtime_Control runtime_control;
|
||||
const Json runtime_encoded = adminive::to_json<Json>(runtime_control);
|
||||
ADMINIVE_CHECK((adminive::json_get<Json, int>(
|
||||
adminive::Json_Adapter<Json>::at(runtime_encoded, "setting")) == 3));
|
||||
Json runtime_patch = adminive::json_object<Json>();
|
||||
adminive::json_set(runtime_patch, "setting", 17);
|
||||
const auto runtime_result =
|
||||
adminive::apply_frontend_patch<Json>(runtime_control, runtime_patch);
|
||||
ADMINIVE_CHECK(runtime_result.success);
|
||||
ADMINIVE_CHECK(runtime_control.setting() == 17);
|
||||
adminive::Collection_Service<Config, Json, adminive::No_Lock> collection("/configs", {Config{}});
|
||||
const auto list = collection.list_response();
|
||||
ADMINIVE_CHECK(list.status == 200);
|
||||
|
||||
Reference in New Issue
Block a user