2D移植完成

This commit is contained in:
2026-09-03 11:52:15 +08:00
parent ee7dd69198
commit 57aff3568c
193 changed files with 3647 additions and 8315 deletions
+29
View File
@@ -0,0 +1,29 @@
#include "Event.hpp"
#include <chrono>
namespace aethera {
namespace {
std::uint64_t event_steady_time_ns() noexcept {
return static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now().time_since_epoch()).count());
}
} // namespace
Event::Event(Event_Type type) : Event(type, Event_Timeline_Time{event_steady_time_ns()}) {}
Event::Event(Event_Type type, Event_Timeline_Time occurred_at) : type(type), occurred_at(occurred_at) {}
Event::~Event() = default;
Event::Event(Event&& other) noexcept : type(other.type), occurred_at(other.occurred_at), accepted(other.accepted) {}
void Event::accept() noexcept {
accepted = true;
}
bool Event::is_accepted() const noexcept {
return accepted;
}
Key_Event::Key_Event(Event_Type type) : Event(type) {}
Key_Event::Key_Event(Event_Type type, Event_Timeline_Time occurred_at) : Event(type, occurred_at) {}
} // namespace aethera
+148
View File
@@ -0,0 +1,148 @@
#pragma once
#include <concepts>
#include <cstdint>
#include <type_traits>
namespace aethera {
enum struct Event_Type : std::uint8_t {
resize,
/* 视口尺寸发生变化。 */
show,
/* 视图进入可见状态。 */
hide,
/* 视图进入隐藏状态。 */
leave,
/* 指针离开当前视图。 */
pointer_move,
/* 指针位置发生变化。 */
pointer_press,
/* 指针按键被按下。 */
pointer_release,
/* 指针按键被释放。 */
wheel,
/* 指针设备产生滚轮输入。 */
key_press,
/* 键盘按键被按下。 */
key_release
/* 键盘按键被释放。 */
};
struct Event_Timeline_Time {
std::uint64_t nanoseconds{}; /* 输入生产者时间线上的事件发生时刻。 */
bool operator==(const Event_Timeline_Time&) const = default;
};
enum struct Mouse_Button : std::uint8_t {
none,
/* 不是鼠标按键事件。 */
left,
/* 鼠标左键。 */
right,
/* 鼠标右键。 */
middle
/* 鼠标中键。 */
};
using Mouse_Button_Mask = std::uint8_t;
enum struct Keyboard_Modifier : std::uint8_t {
none = 0,
/* 没有修饰键。 */
control = 1 << 0,
/* Control 修饰键。 */
shift = 1 << 1,
/* Shift 修饰键。 */
alt = 1 << 2,
/* Alt 修饰键。 */
meta = 1 << 3
/* 平台 Meta 修饰键。 */
};
[[nodiscard]] constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept;
enum struct Key : std::uint16_t {
unknown,
/* 未映射的按键。 */
escape,
/* Escape。 */
enter,
/* Enter。 */
space,
/* Space。 */
delete_key,
/* Delete。 */
backspace,
/* Backspace。 */
left,
/* 左方向键。 */
right,
/* 右方向键。 */
up,
/* 上方向键。 */
down,
/* 下方向键。 */
home
/* Home。 */
};
/* Scene 独占接收的事件基类;accepted 只由 Scene 的 advance 线程修改。 */
struct Event {
explicit Event(Event_Type type);
Event(Event_Type type, Event_Timeline_Time occurred_at);
virtual ~Event();
Event(const Event&) = delete;
Event& operator=(const Event&) = delete;
Event(Event&&) noexcept;
Event& operator=(Event&&) noexcept = delete;
void accept() noexcept;
[[nodiscard]] bool is_accepted() const noexcept;
const Event_Type type; /* 事件的业务类型。 */
const Event_Timeline_Time occurred_at; /* 输入生产者提供的发生时刻。 */
private:
bool accepted{}; /* 分发链是否已经消费事件;没有跨线程访问。 */
};
template <typename Type>
concept Event_Object = std::derived_from<std::remove_cvref_t<Type>, Event>;
template <typename Type>
concept Event_Point = std::default_initializable<Type> && requires(Type value) { value.x; value.y; };
template <typename Type>
concept Event_Size = std::default_initializable<Type> && requires(Type value) { value.width; value.height; };
template <Event_Point Point>
struct Basic_Pointer_Event : Event {
explicit Basic_Pointer_Event(Event_Type type = Event_Type::pointer_move);
Basic_Pointer_Event(Event_Type type, Event_Timeline_Time occurred_at);
Point position{}; /* 接收对象局部坐标。 */
Point global_position{}; /* 全局窗口坐标。 */
Mouse_Button button{Mouse_Button::none}; /* 本次按下或释放的按键。 */
Mouse_Button_Mask buttons{}; /* 事件发生时保持按下的按键集合。 */
Keyboard_Modifier modifiers{Keyboard_Modifier::none}; /* 事件发生时的修饰键集合。 */
};
template <Event_Point Point>
struct Basic_Wheel_Event : Basic_Pointer_Event<Point> {
Basic_Wheel_Event();
explicit Basic_Wheel_Event(Event_Timeline_Time occurred_at);
double angle_delta_x{}; /* 水平方向滚轮角度增量。 */
double angle_delta_y{}; /* 垂直方向滚轮角度增量。 */
double pixel_delta_x{}; /* 水平方向高精度像素增量。 */
double pixel_delta_y{}; /* 垂直方向高精度像素增量。 */
};
template <Event_Size Size>
struct Basic_Resize_Event : Event {
Basic_Resize_Event();
explicit Basic_Resize_Event(Event_Timeline_Time occurred_at);
Size old_size{}; /* 调整前尺寸。 */
Size new_size{}; /* 调整后尺寸。 */
};
struct Key_Event : Event {
explicit Key_Event(Event_Type type);
Key_Event(Event_Type type, Event_Timeline_Time occurred_at);
Key key{Key::unknown}; /* 标准化按键。 */
std::uint32_t native_key{}; /* 平台原生按键编码。 */
Keyboard_Modifier modifiers{Keyboard_Modifier::none}; /* 事件发生时的修饰键集合。 */
bool auto_repeat{}; /* 是否由系统自动重复产生。 */
};
} // namespace aethera
#include "Event.ipp"
+24
View File
@@ -0,0 +1,24 @@
#pragma once
namespace aethera {
constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, Keyboard_Modifier right) noexcept {
return static_cast<Keyboard_Modifier>(static_cast<std::uint8_t>(left) | static_cast<std::uint8_t>(right));
}
template <Event_Point Point>
Basic_Pointer_Event<Point>::Basic_Pointer_Event(Event_Type type) : Event(type) {}
template <Event_Point Point>
Basic_Pointer_Event<Point>::Basic_Pointer_Event(Event_Type type, Event_Timeline_Time occurred_at) : Event(type, occurred_at) {}
template <Event_Point Point>
Basic_Wheel_Event<Point>::Basic_Wheel_Event() : Basic_Pointer_Event<Point>(Event_Type::wheel) {}
template <Event_Point Point>
Basic_Wheel_Event<Point>::Basic_Wheel_Event(Event_Timeline_Time occurred_at) : Basic_Pointer_Event<Point>(Event_Type::wheel, occurred_at) {}
template <Event_Size Size>
Basic_Resize_Event<Size>::Basic_Resize_Event() : Event(Event_Type::resize) {}
template <Event_Size Size>
Basic_Resize_Event<Size>::Basic_Resize_Event(Event_Timeline_Time occurred_at) : Event(Event_Type::resize, occurred_at) {}
} // namespace aethera
+3
View File
@@ -34,6 +34,9 @@ PRO_MEM_DISPATCH(schedule_every);
PRO_MEM_DISPATCH(reschedule);
PRO_MEM_DISPATCH(cancel);
PRO_MEM_DISPATCH(send);
PRO_MEM_DISPATCH(event_routing_distance);
PRO_MEM_DISPATCH(dispatch_event);
PRO_MEM_DISPATCH(composite);
struct Non_Copyable {
protected:
Non_Copyable() = default;
+41 -18
View File
@@ -20,20 +20,20 @@ template <typename Entries, typename Protocols = Model_Type_List<>>
struct Collect_Model_Protocols;
template <typename Tag, typename Layer, typename = void>
struct Model_Tag_Value;
template <typename Tag, typename Registrations>
struct Model_Registration;
template <typename Tag, typename Layer, typename Registrations>
using Model_Value = typename Model_Registration<Tag, Registrations>::Type::template Value<Layer>;
template <typename Tag, typename Entries>
struct Find_Model_Entry;
template <typename Tag, typename Layer, typename Entries>
using Model_Value = typename Find_Model_Entry<Tag, Entries>::Type::template Value<Layer>;
template <typename Tag, typename Layer>
using Registered_Model_Value = Model_Value<Tag, Layer, typename Layer::Model_Registrations>;
using Materialized_Model_Value = Model_Value<Tag, Layer, typename Layer::Model_Entries>;
template <typename Tag, typename Layer>
using Registered_Model_Attachment = typename Model_Registration<Tag, typename Layer::Model_Registrations>::Type::template Attachment<Layer>;
using Materialized_Model_Attachment = typename Find_Model_Entry<Tag, typename Layer::Model_Entries>::Type::template Attachment<Layer>;
template <typename Tag, typename Endpoint, typename Protocol, typename = void>
struct Is_Model_Tag_Protocol : std::false_type {};
template <typename Tag, typename Endpoint, typename Protocol> concept Model_Tag_Uses_Protocol = Is_Model_Tag_Protocol<Tag, Endpoint, Protocol>::value;
template <typename Registration> concept Model_Registration_Group = requires { typename Registration::Entries; };
template <typename Definition> concept Model_Attachment_Definition = requires { typename Definition::Entries; };
/* 三种选择器共用一套递归,避免为 Model、Builder、Private 分别维护组合算法。 */
struct Select_Model_API;
struct Select_Model_Builder_API;
@@ -42,11 +42,15 @@ template <typename Selector, typename Endpoint, typename Base, typename Protocol
struct Compose_Model_APIs;
struct Empty_Model_Value {}; /* 某 Tag 在首个定义层没有前驱值时使用的空根。 */
template <typename Endpoint, typename Registrations>
template <typename Endpoint, typename Entries>
struct Endpoint_Attachments;
template <typename Endpoint>
struct Model_State;
template <typename Self, typename Base, typename... Registrations>
template <typename Endpoint, typename Tag>
struct Concurrent_Private_View;
template <typename Endpoint, typename Tag>
struct Concurrent_Const_Private_View;
template <typename Self, typename Base, typename... Definitions>
struct Model_Definition;
/* 所有 Private 层通过这个虚基类指向 Root 唯一拥有的 endpoint state。 */
struct Model_Instance;
@@ -61,6 +65,9 @@ struct Model_Private_Base;
template <typename Endpoint> typename Endpoint::Private& model_private(Endpoint& model) noexcept;
template <typename Endpoint> const typename Endpoint::Private& model_private(const Endpoint& model) noexcept;
template <typename Endpoint> inline constexpr unsigned char Model_Private_Type_Token{};
template <typename Tag> inline constexpr unsigned char Model_Tag_Type_Token{};
template <typename Tag, typename Endpoint> auto& model_attachment(Endpoint& model) noexcept;
template <typename Tag, typename Endpoint> const auto& model_attachment(const Endpoint& model) noexcept;
template <typename Model_Pointer>
struct Model_Private_Pointer {
@@ -74,6 +81,10 @@ struct Model_State_Operations {
void operator()(void* value) const noexcept;
void (*destroy)(void*) noexcept {}; /* 与被擦除的实际 endpoint state 匹配。 */
void* (*resolve_private)(void*, const void*) noexcept {}; /* 将 Root 唯一 state 调整为请求层的 Private。 */
void (*advance_concurrent)(void*, const void*) {}; /* 按 Tag 推进实际 endpoint 的并发附件。 */
void* (*resolve_concurrent_value)(void*, const void*, const void*) noexcept {}; /* 将实际值调整为请求定义层的值基类。 */
const void* (*resolve_const_concurrent_value)(const void*, const void*, const void*) noexcept {}; /* 只读解析不得触发附件写访问。 */
void (*write_concurrent_value)(void*, const void*, const void*, void (*)(void*, void*), void*) {}; /* 在实际附件写入边界编辑请求层值。 */
};
struct Model_Instance {
@@ -92,10 +103,22 @@ struct Model_Instance {
friend struct Model_Builder_Instance;
template <typename Endpoint> friend typename Endpoint::Private& model_private(Endpoint& model) noexcept;
template <typename Endpoint> friend const typename Endpoint::Private& model_private(const Endpoint& model) noexcept;
template <typename Tag, typename Endpoint> friend auto& model_attachment(Endpoint& model) noexcept;
template <typename Tag, typename Endpoint> friend const auto& model_attachment(const Endpoint& model) noexcept;
template <typename, typename>
friend struct Concurrent_Private_View;
template <typename, typename>
friend struct Concurrent_Const_Private_View;
template <typename Endpoint, typename... Args>
void initialize_endpoint(Args&&... args);
template <typename Endpoint>
Model_State<Endpoint>& state() noexcept;
Model_State<Endpoint>& state() noexcept;
template <typename Endpoint>
const Model_State<Endpoint>& state() const noexcept;
void advance_concurrent(const void* tag);
[[nodiscard]] void* resolve_concurrent_value(const void* tag, const void* layer) const noexcept;
[[nodiscard]] const void* resolve_const_concurrent_value(const void* tag, const void* layer) const noexcept;
void write_concurrent_value(const void* tag, const void* layer, void (*write)(void*, void*), void* context);
std::unique_ptr<void, Model_State_Operations> state_owner; /* 唯一拥有实际 Private、附件及类型操作。 */
};
@@ -121,8 +144,8 @@ struct State_Tag {
};
struct Root : detail::Model_Instance {
using Model_Layers = detail::Model_Type_List<>;
using Model_Registrations = detail::Model_Type_List<>;
using Model_Layers = detail::Model_Type_List<>;
using Model_Entries = detail::Model_Type_List<>;
struct Private {};
protected:
@@ -131,15 +154,15 @@ struct Root : detail::Model_Instance {
};
/* 业务定义直接使用 Prev、Prev_Private 和 Builder;其余别名只供框架递归。 */
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
struct Def : detail::Model_Definition<Self, Base, Registrations...>::Public_Base {
template <typename Self, typename Base, detail::Model_Attachment_Definition... Definitions>
struct Def : detail::Model_Definition<Self, Base, Definitions...>::Public_Base {
private:
using Definition = detail::Model_Definition<Self, Base, Registrations...>;
using Definition = detail::Model_Definition<Self, Base, Definitions...>;
public:
using Model_Layers = typename detail::Merge_Model_Type_Lists<typename Base::Model_Layers, detail::Model_Type_List<Self>>::Type;
using Model_Registrations = typename Definition::Registration_List;
using Model_Layers = typename detail::Merge_Model_Type_Lists<typename Base::Model_Layers, detail::Model_Type_List<Self>>::Type;
using Model_Entries = typename Definition::Entry_List;
template <typename Tag>
using Prev = detail::Model_Value<Tag, Base, Model_Registrations>;
using Prev = detail::Model_Value<Tag, Base, Model_Entries>;
using Prev_Private = typename Definition::Private_Base;
struct Builder : Definition::Builder_Base {
+171 -19
View File
@@ -42,19 +42,19 @@ struct Model_Tag_Value<Tag, Layer, std::void_t<typename Tag::template Value<Laye
using Type = typename Tag::template Value<Layer>;
};
/* 从完整注册链中定位 Tag;没有匹配项时保持未定义,让 requires 正常拒绝调用。 */
/* 从完整附件项链中定位 Tag;没有匹配项时保持未定义,让 requires 正常拒绝调用。 */
template <typename Tag, typename Entry, typename... Rest> requires std::same_as<Tag, typename Entry::Tag_Type>
struct Model_Registration<Tag, Model_Type_List<Entry, Rest...>> {
struct Find_Model_Entry<Tag, Model_Type_List<Entry, Rest...>> {
using Type = Entry;
};
template <typename Tag, typename Entry, typename... Rest> requires (!std::same_as<Tag, typename Entry::Tag_Type>)
struct Model_Registration<Tag, Model_Type_List<Entry, Rest...>> : Model_Registration<Tag, Model_Type_List<Rest...>> {};
struct Find_Model_Entry<Tag, Model_Type_List<Entry, Rest...>> : Find_Model_Entry<Tag, Model_Type_List<Rest...>> {};
template <typename Tag, typename Endpoint, typename Protocol>
struct Is_Model_Tag_Protocol<Tag, Endpoint, Protocol, std::void_t<typename Model_Registration<Tag, typename Endpoint::Model_Registrations>::Type>> : std::bool_constant<std::same_as<typename Model_Registration<Tag, typename Endpoint::Model_Registrations>::Type::Protocol, Protocol>> {};
struct Is_Model_Tag_Protocol<Tag, Endpoint, Protocol, std::void_t<typename Find_Model_Entry<Tag, typename Endpoint::Model_Entries>::Type>> : std::bool_constant<std::same_as<typename Find_Model_Entry<Tag, typename Endpoint::Model_Entries>::Type::Protocol, Protocol>> {};
template <typename Registrations>
template <typename Entries>
struct Unique_Model_Tags;
template <>
@@ -64,13 +64,13 @@ template <typename Entry, typename... Rest>
struct Unique_Model_Tags<Model_Type_List<Entry, Rest...>> : std::bool_constant<(!std::same_as<typename Entry::Tag_Type, typename Rest::Tag_Type> && ...) && Unique_Model_Tags<Model_Type_List<Rest...>>::value> {};
template <typename Endpoint, typename Entry>
struct Materialized_Model_Attachment {
struct Materialized_Model_Entry {
static_assert(Entry::template Valid<Endpoint>, "registered attachment does not satisfy its protocol");
using Type = Model_Attachment<typename Entry::Tag_Type, typename Entry::template Attachment<Endpoint>>;
};
template <typename Endpoint, typename... Entries>
struct Endpoint_Attachments<Endpoint, Model_Type_List<Entries...>> : Model_Attachment_Set<typename Materialized_Model_Attachment<Endpoint, Entries>::Type...> {
struct Endpoint_Attachments<Endpoint, Model_Type_List<Entries...>> : Model_Attachment_Set<typename Materialized_Model_Entry<Endpoint, Entries>::Type...> {
static_assert(Unique_Model_Tags<Model_Type_List<Entries...>>::value, "each model attachment tag can only be registered once");
};
@@ -100,11 +100,11 @@ struct Compose_Model_APIs<Selector, Endpoint, Base, Model_Type_List<Protocol, Re
using Type = typename Compose_Model_APIs<Selector, Endpoint, Extended_Base, Model_Type_List<Rest...>>::Type;
};
/* 一次计算当前 Def 的注册链和协议表,三类 API 都从同一结果组合。 */
template <typename Self, typename Base, typename... Registrations>
/* 一次计算当前 Def 的附件项链和协议表,三类 API 都从同一结果组合。 */
template <typename Self, typename Base, typename... Definitions>
struct Model_Definition {
using Registration_List = typename Merge_Model_Type_Lists<typename Base::Model_Registrations, typename Registrations::Entries...>::Type;
using Protocol_List = typename Collect_Model_Protocols<Registration_List>::Type;
using Entry_List = typename Merge_Model_Type_Lists<typename Base::Model_Entries, typename Definitions::Entries...>::Type;
using Protocol_List = typename Collect_Model_Protocols<Entry_List>::Type;
using Public_Base = typename Compose_Model_APIs<Select_Model_API, Self, Base, Protocol_List>::Type;
using Private_Core = Model_Private_Base<Self, typename Base::Private>;
using Private_Base = typename Compose_Model_APIs<Select_Model_Private_API, Self, Private_Core, Protocol_List>::Type;
@@ -121,7 +121,7 @@ struct Model_Private_Base : Base_Private, virtual Model_Private_Access_Root {
};
template <typename Endpoint>
struct Model_State : Endpoint_Attachments<Endpoint, typename Endpoint::Model_Registrations> {
struct Model_State : Endpoint_Attachments<Endpoint, typename Endpoint::Model_Entries> {
template <typename... Args>
explicit Model_State(Args&&... args);
typename Endpoint::Private private_data; /* 唯一实际 Private,包含完整的 Private 继承链。 */
@@ -146,6 +146,127 @@ void* resolve_model_state_private(void* state, const void* token) noexcept {
return resolve_model_private<Endpoint>(state, token, typename Endpoint::Model_Layers{});
}
template <typename Entry, typename Active_Endpoint, typename Layer>
void* adjust_model_value_to_layer(typename Entry::template Value<Active_Endpoint>* value, const void* layer) noexcept {
using Active_Value = typename Entry::template Value<Active_Endpoint>;
using Layer_Value = typename Model_Tag_Value<typename Entry::Tag_Type, Layer>::Type;
if constexpr (!std::same_as<Layer_Value, Empty_Model_Value> && std::derived_from<Active_Value, Layer_Value>) {
if (layer == &Model_Private_Type_Token<Layer>) return static_cast<Layer_Value*>(value);
}
return nullptr;
}
template <typename Entry, typename Active_Endpoint, typename... Layers>
void* adjust_model_value_to_layer(typename Entry::template Value<Active_Endpoint>* value, const void* layer, Model_Type_List<Layers...>) noexcept {
void* result{};
((result ? result : result = adjust_model_value_to_layer<Entry, Active_Endpoint, Layers>(value, layer)), ...);
return result;
}
template <typename Entry, typename Active_Endpoint, typename Layer>
const void* adjust_const_model_value_to_layer(const typename Entry::template Value<Active_Endpoint>* value, const void* layer) noexcept {
using Active_Value = typename Entry::template Value<Active_Endpoint>;
using Layer_Value = typename Model_Tag_Value<typename Entry::Tag_Type, Layer>::Type;
if constexpr (!std::same_as<Layer_Value, Empty_Model_Value> && std::derived_from<Active_Value, Layer_Value>) {
if (layer == &Model_Private_Type_Token<Layer>) return static_cast<const Layer_Value*>(value);
}
return nullptr;
}
template <typename Entry, typename Active_Endpoint, typename... Layers>
const void* adjust_const_model_value_to_layer(const typename Entry::template Value<Active_Endpoint>* value, const void* layer, Model_Type_List<Layers...>) noexcept {
const void* result{};
((result ? result : result = adjust_const_model_value_to_layer<Entry, Active_Endpoint, Layers>(value, layer)), ...);
return result;
}
template <typename Active_Endpoint, typename Entry>
void advance_model_concurrent_entry(Model_State<Active_Endpoint>& state, const void* tag) {
if (tag != &Model_Tag_Type_Token<typename Entry::Tag_Type>) return;
auto& attachment = state.template get<typename Entry::Tag_Type>();
if constexpr (requires { attachment.internal.advance(); }) attachment.internal.advance();
}
template <typename Active_Endpoint, typename... Entries>
void advance_model_concurrent(void* state, const void* tag, Model_Type_List<Entries...>) {
auto& typed_state = *static_cast<Model_State<Active_Endpoint>*>(state);
(advance_model_concurrent_entry<Active_Endpoint, Entries>(typed_state, tag), ...);
}
template <typename Active_Endpoint, typename Entry>
void* resolve_model_concurrent_entry(Model_State<Active_Endpoint>& state, const void* tag, const void* layer) noexcept {
if (tag != &Model_Tag_Type_Token<typename Entry::Tag_Type>) return nullptr;
auto& attachment = state.template get<typename Entry::Tag_Type>();
if constexpr (requires { attachment.internal.use(); }) {
auto* value = attachment.internal.use();
return adjust_model_value_to_layer<Entry, Active_Endpoint>(value, layer, typename Active_Endpoint::Model_Layers{});
}
return nullptr;
}
template <typename Active_Endpoint, typename... Entries>
void* resolve_model_concurrent(void* state, const void* tag, const void* layer, Model_Type_List<Entries...>) noexcept {
auto& typed_state = *static_cast<Model_State<Active_Endpoint>*>(state);
void* result{};
((result ? result : result = resolve_model_concurrent_entry<Active_Endpoint, Entries>(typed_state, tag, layer)), ...);
return result;
}
template <typename Active_Endpoint, typename Entry>
const void* resolve_const_model_concurrent_entry(const Model_State<Active_Endpoint>& state, const void* tag, const void* layer) noexcept {
if (tag != &Model_Tag_Type_Token<typename Entry::Tag_Type>) return nullptr;
const auto& attachment = state.template get<typename Entry::Tag_Type>();
if constexpr (requires { attachment.internal.use(); }) {
const auto* value = attachment.internal.use();
return adjust_const_model_value_to_layer<Entry, Active_Endpoint>(value, layer, typename Active_Endpoint::Model_Layers{});
}
return nullptr;
}
template <typename Active_Endpoint, typename... Entries>
const void* resolve_const_model_concurrent(const void* state, const void* tag, const void* layer, Model_Type_List<Entries...>) noexcept {
const auto& typed_state = *static_cast<const Model_State<Active_Endpoint>*>(state);
const void* result{};
((result ? result : result = resolve_const_model_concurrent_entry<Active_Endpoint, Entries>(typed_state, tag, layer)), ...);
return result;
}
template <typename Active_Endpoint, typename Entry>
void write_model_concurrent_entry(Model_State<Active_Endpoint>& state, const void* tag, const void* layer, void (*write)(void*, void*), void* context) {
if (tag != &Model_Tag_Type_Token<typename Entry::Tag_Type>) return;
auto& attachment = state.template get<typename Entry::Tag_Type>();
auto operation = [&](auto& value) {
write(adjust_model_value_to_layer<Entry, Active_Endpoint>(std::addressof(value), layer, typename Active_Endpoint::Model_Layers{}), context);
};
if constexpr (requires { attachment.set(operation); }) attachment.set(operation);
}
template <typename Active_Endpoint, typename... Entries>
void write_model_concurrent(void* state, const void* tag, const void* layer, void (*write)(void*, void*), void* context, Model_Type_List<Entries...>) {
auto& typed_state = *static_cast<Model_State<Active_Endpoint>*>(state);
(write_model_concurrent_entry<Active_Endpoint, Entries>(typed_state, tag, layer, write, context), ...);
}
template <typename Endpoint>
void advance_model_state_concurrent(void* state, const void* tag) {
advance_model_concurrent<Endpoint>(state, tag, typename Endpoint::Model_Entries{});
}
template <typename Endpoint>
void* resolve_model_state_concurrent(void* state, const void* tag, const void* layer) noexcept {
return resolve_model_concurrent<Endpoint>(state, tag, layer, typename Endpoint::Model_Entries{});
}
template <typename Endpoint>
const void* resolve_const_model_state_concurrent(const void* state, const void* tag, const void* layer) noexcept {
return resolve_const_model_concurrent<Endpoint>(state, tag, layer, typename Endpoint::Model_Entries{});
}
template <typename Endpoint>
void write_model_state_concurrent(void* state, const void* tag, const void* layer, void (*write)(void*, void*), void* context) {
write_model_concurrent<Endpoint>(state, tag, layer, write, context, typename Endpoint::Model_Entries{});
}
inline Model_Instance::Model_Instance() : state_owner(nullptr, Model_State_Operations{}) {}
inline Model_Instance::~Model_Instance() = default;
@@ -178,7 +299,7 @@ template <typename Endpoint, typename... Args>
void Model_Instance::initialize_endpoint(Args&&... args) {
auto state = std::make_unique<Model_State<Endpoint>>(std::forward<Args>(args)...);
static_cast<Model_Private_Access_Root&>(state->private_data).instance = this;
state_owner = std::unique_ptr<void, Model_State_Operations>{state.release(), Model_State_Operations{destroy_model_state<Endpoint>, resolve_model_state_private<Endpoint>}};
state_owner = std::unique_ptr<void, Model_State_Operations>{state.release(), Model_State_Operations{destroy_model_state<Endpoint>, resolve_model_state_private<Endpoint>, advance_model_state_concurrent<Endpoint>, resolve_model_state_concurrent<Endpoint>, resolve_const_model_state_concurrent<Endpoint>, write_model_state_concurrent<Endpoint>}};
}
template <typename Endpoint>
@@ -186,6 +307,37 @@ Model_State<Endpoint>& Model_Instance::state() noexcept {
return *static_cast<Model_State<Endpoint>*>(state_owner.get());
}
template <typename Endpoint>
const Model_State<Endpoint>& Model_Instance::state() const noexcept {
return *static_cast<const Model_State<Endpoint>*>(state_owner.get());
}
inline void Model_Instance::advance_concurrent(const void* tag) {
state_owner.get_deleter().advance_concurrent(state_owner.get(), tag);
}
inline void* Model_Instance::resolve_concurrent_value(const void* tag, const void* layer) const noexcept {
return state_owner.get_deleter().resolve_concurrent_value(state_owner.get(), tag, layer);
}
inline const void* Model_Instance::resolve_const_concurrent_value(const void* tag, const void* layer) const noexcept {
return state_owner.get_deleter().resolve_const_concurrent_value(state_owner.get(), tag, layer);
}
inline void Model_Instance::write_concurrent_value(const void* tag, const void* layer, void (*write)(void*, void*), void* context) {
state_owner.get_deleter().write_concurrent_value(state_owner.get(), tag, layer, write, context);
}
template <typename Tag, typename Endpoint>
auto& model_attachment(Endpoint& model) noexcept {
return static_cast<Model_Instance&>(model).template state<Endpoint>().template get<Tag>();
}
template <typename Tag, typename Endpoint>
const auto& model_attachment(const Endpoint& model) noexcept {
return static_cast<const Model_Instance&>(model).template state<Endpoint>().template get<Tag>();
}
template <typename Endpoint>
typename Endpoint::Private& model_private(Endpoint& model) noexcept {
auto& instance = static_cast<Model_Instance&>(model);
@@ -226,19 +378,19 @@ std::unique_ptr<Self> Model_Builder_Instance<Self>::release_model() {
}
} // namespace aethera::detail
namespace aethera {
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
template <typename Self, typename Base, detail::Model_Attachment_Definition... Definitions>
template <typename First, typename... Rest>
Def<Self, Base, Registrations...>::Builder::Builder(First&& first, Rest&&... rest) {
Def<Self, Base, Definitions...>::Builder::Builder(First&& first, Rest&&... rest) {
this->initialize_model(std::forward<First>(first), std::forward<Rest>(rest)...);
}
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
std::unique_ptr<Self> Def<Self, Base, Registrations...>::Builder::build() {
template <typename Self, typename Base, detail::Model_Attachment_Definition... Definitions>
std::unique_ptr<Self> Def<Self, Base, Definitions...>::Builder::build() {
return this->release_model();
}
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
bool Def<Self, Base, Registrations...>::model_initialized() const noexcept {
template <typename Self, typename Base, detail::Model_Attachment_Definition... Definitions>
bool Def<Self, Base, Definitions...>::model_initialized() const noexcept {
return this->initialized();
}
@@ -1,5 +1,6 @@
#pragma once
#include "../../../global.hpp"
#include "../../detail/Relation_Definition.hpp"
#include <cstdint>
#include <expected>
#include <functional>
@@ -80,7 +81,9 @@ struct Dag_Relation<Node_Facade>::Editor {
};
/* Model 可注册的拥有型 DAG 关系;内部 commit 在所有者安全点验证并提交待处理事务。 */
template <typename Node_Facade>
struct Owned_Dag_Relation {
struct Owned_Dag_Relation : detail::Relation_Definition<Owned_Dag_Relation<Node_Facade>, Node_Facade> {
template <typename, typename Tag>
using Attachment = Owned_Dag_Relation<Tag>;
using Graph = Dag_Relation<Node_Facade>;
using Edit = std::move_only_function<void(typename Graph::Editor&)>;
using Edit_Completion = std::move_only_function<void(std::expected<void, Dag_Result>)>;
@@ -2,10 +2,10 @@
Model 的可组合能力统一分为两层:
- `model/registration` 保存协议 concept、注册项和 Model/Builder/Private 三组 CRTP API。
- `model/attachment` 保存满足协议的具体附件实现
- `model/detail` 保存公共协议 concept 和 Model/Builder/Private 三组 CRTP API。
- `model/attachment` 的每个具体类型同时定义注册项、实际存储和自己的能力来源
`Concurrent_Registration``Relation_Registration` 是同级协议。并发附件使用 `use/advance/builder_set` 和可选的外部 `set/get`;关系附件使用事务 `edit`、Builder 初始化 `relation` 和所有者安全点 `relation/commit`。DAG 不复用并发接口。
`Concurrent_Definition``Relation_Definition` 是同级 CRTP 定义基类。并发附件使用 `use/advance/builder_set` 和可选的外部 `set/get`;关系附件使用事务 `edit`、Builder 初始化 `relation` 和所有者安全点 `relation/commit`。DAG 不复用并发接口。
## 具体附件
@@ -18,12 +18,12 @@ Model 的可组合能力统一分为两层:
## 并发附件契约
每个注册进 Model 的并发附件必须提供内部 `internal.use()``internal.advance()``internal.builder_set(...)`,外部 `set(...)``get(...)` 至少提供一个。`Concurrent_Registration<Concrete, Tags...>` 向最终 endpoint 选择性附加实体 `set<Tag>/get<Tag>`、Builder `set<Tag>` 和 Private `concurrent<Tag>()`
每个注册进 Model 的并发附件必须提供内部 `internal.use()``internal.advance()``internal.builder_set(...)`,外部 `set(...)``get(...)` 至少提供一个。`Import_Struct_Concurrent<Tags...>` 等一段式定义直接向最终 endpoint 选择性附加实体 `set<Tag>/get<Tag>`、Builder `set<Tag>` 和 Private `concurrent<Tag>()`,并直接定义各 Tag 的实际存储
并发附件不提供同步等待、事件或快照。内部 `use()/advance()` 必须由同一内部线程串行调用;`use()` 返回的非拥有借用不得跨越下一次 `advance()`。外部回调在对应交换锁内执行,不得重入当前附件。
并发附件不提供同步等待、事件或快照。内部 `use()/advance()` 必须由同一内部线程串行调用;`use()` 返回的非拥有借用不得跨越下一次 `advance()`。外部回调在对应交换锁内执行,不得重入当前附件。Private 继承链中的 `concurrent<Tag>()` 是临时访问视图,由 Root 解析最终 endpoint 的唯一实际附件;需要检查附件实体本身时使用最终 Model,而不是保存该临时视图。
## 关系附件契约
关系附件必须提供权威 `Graph`、事务 `Edit`、事务完成回调,以及内部 `relation()``commit()``build(...)``Relation_Registration<Relation, Tags...>` 向最终 endpoint 选择性附加实体 `edit<Tag>`、Builder `relation<Tag>` 和 Private `relation<Tag>/commit<Tag>`
关系附件必须提供权威 `Graph`、事务 `Edit`、事务完成回调,以及内部 `relation()``commit()``build(...)``Owned_Dag_Relation<Node_Facade>` 自身是一段式定义和实际附件,向最终 endpoint 选择性附加实体 `edit<Tag>`、Builder `relation<Tag>` 和 Private `relation<Tag>/commit<Tag>`
`Dag_Relation::Builder` 在构建期验证完整关系;`Dag_Relation::Editor` 只记录单次事务命令;`Owned_Dag_Relation` 由关系所有者在安全点验证并提交。编辑 completion 在调用 `commit()` 的内部线程执行,不是跨线程异常传播边界。
@@ -49,16 +49,20 @@ struct Struct_Dirty_Concurrent_Hooks : Dirty_Concurrent_Hooks {
bool pending_whole_property_dirty{}; /* 当前编辑面是否发生过整对象写入。 */
};
}
template <Exchange_Value Value>
using Dirty_Export_Struct_Concurrent = Export_Struct_Concurrent<Value, detail::Struct_Dirty_Concurrent_Hooks>;
template <Exchange_Value Value>
using Dirty_Import_Struct_Concurrent = Import_Struct_Concurrent<Value, detail::Struct_Dirty_Concurrent_Hooks>;
template <List_Value Value>
using Dirty_Import_List_Concurrent = Import_List_Concurrent<Value, detail::Dirty_Concurrent_Hooks>;
template <List_Value Value>
using Dirty_Export_List_Concurrent = Export_List_Concurrent<Value, detail::Dirty_Concurrent_Hooks>;
template <List_Value Value>
using Dirty_Readable_Import_Batch_Concurrent = Readable_Import_Batch_Concurrent<Value, detail::Dirty_Concurrent_Hooks>;
template <typename... Tags>
struct Dirty_Export_Struct_Concurrent : detail::Export_Struct_Concurrent_Definition<Dirty_Export_Struct_Concurrent<Tags...>, detail::Struct_Dirty_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Dirty_Import_Struct_Concurrent : detail::Import_Struct_Concurrent_Definition<Dirty_Import_Struct_Concurrent<Tags...>, detail::Struct_Dirty_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Dirty_Import_List_Concurrent : detail::Import_List_Concurrent_Definition<Dirty_Import_List_Concurrent<Tags...>, detail::Dirty_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Dirty_Export_List_Concurrent : detail::Export_List_Concurrent_Definition<Dirty_Export_List_Concurrent<Tags...>, detail::Dirty_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Dirty_Readable_Import_Batch_Concurrent : detail::Readable_Import_Batch_Concurrent_Definition<Dirty_Readable_Import_Batch_Concurrent<Tags...>, detail::Dirty_Concurrent_Hooks, Tags...> {};
template <typename Source>concept Dirty_Revision_Source = requires(const Source& source) {
{ source.internal.dirty_revision() } noexcept -> std::same_as<std::uint64_t>;
};
@@ -1,72 +1,172 @@
#pragma once
#include "../../../global.hpp"
#include "../../registration/Concurrent_Registration.hpp"
#include "../../detail/Concurrent_Definition.hpp"
#include <functional>
#include <mutex>
#include <utility>
namespace aethera {
namespace detail {
/* 外部写入批次,内部每次 advance 取走整批数据。 */
template <List_Value Value, typename Concurrent_Hooks = detail::No_Concurrent_Hooks>
struct Import_List_Concurrent {
template <Value_Writer<Value> Fn>
void set(Fn&& fn);
template <typename Definition, typename Concurrent_Hooks, typename... Tags>
struct Import_List_Concurrent_Definition : Concurrent_Definition<Definition, Tags...> {
template <typename Endpoint, typename Tag>
struct Attachment {
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
static_assert(List_Value<Value>, "import list value must satisfy List_Value");
struct Internal final : Concurrent_Hooks {
/* 仅同一内部线程可调用;返回借用不得保存到下一次 advance。 */
Value* use();
void advance();
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn);
Value buf[2]{}; /* 两个循环复用的列表槽位。 */
Value* internal{&buf[0]}; /* 当前内部角色的非拥有借用。 */
Value* external{&buf[1]}; /* 当前外部角色的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护外部访问、角色交换和复用槽清理。 */
};
void set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) internal.on_write_access();
std::invoke(std::forward<Fn>(fn), *internal.external);
}
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
struct Internal final : Concurrent_Hooks {
Value* use() {
return internal;
}
const Value* use() const {
return internal;
}
void advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = internal;
Value* const external_before = external;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
std::swap(internal, external);
external->clear();
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal);
}
Value buf[2]{}; /* 两个循环复用的列表槽位。 */
Value* internal{&buf[0]}; /* 当前内部角色的非拥有借用。 */
Value* external{&buf[1]}; /* 当前外部角色的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护外部访问、角色交换和复用槽清理。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
};
/* 内部生产批次,外部读取最近一次 advance 发布的整批数据。 */
template <List_Value Value, typename Concurrent_Hooks = detail::No_Concurrent_Hooks>
struct Export_List_Concurrent {
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const;
template <typename Definition, typename Concurrent_Hooks, typename... Tags>
struct Export_List_Concurrent_Definition : Concurrent_Definition<Definition, Tags...> {
template <typename Endpoint, typename Tag>
struct Attachment {
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
static_assert(List_Value<Value>, "export list value must satisfy List_Value");
struct Internal final : Concurrent_Hooks {
/* 仅同一内部线程可调用;可写借用会记录本轮修改,且不得跨越 advance。 */
Value* use();
void advance();
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn);
Value buf[2]{}; /* 两个循环复用的列表槽位。 */
Value* internal{&buf[0]}; /* 当前内部角色的非拥有借用。 */
Value* external{&buf[1]}; /* 当前外部角色的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护外部访问、角色交换和复用槽清理。 */
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(*internal.external));
}
struct Internal final : Concurrent_Hooks {
Value* use() {
if constexpr (requires { this->on_write_access(); }) this->on_write_access();
return internal;
}
const Value* use() const {
return internal;
}
void advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = internal;
Value* const external_before = external;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
std::swap(internal, external);
internal->clear();
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal);
}
Value buf[2]{}; /* 两个循环复用的列表槽位。 */
Value* internal{&buf[0]}; /* 当前内部角色的非拥有借用。 */
Value* external{&buf[1]}; /* 当前外部角色的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护外部访问、角色交换和复用槽清理。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
/* 外部写入批次,内部消费后保留上一批供外部诊断读取。 */
template <List_Value Value, typename Concurrent_Hooks = detail::No_Concurrent_Hooks>
struct Readable_Import_Batch_Concurrent {
template <Value_Writer<Value> Fn>
void set(Fn&& fn);
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const;
/* 外部写入批次,内部消费后保留上一批供外部诊断读取。 */
template <typename Definition, typename Concurrent_Hooks, typename... Tags>
struct Readable_Import_Batch_Concurrent_Definition : Concurrent_Definition<Definition, Tags...> {
template <typename Endpoint, typename Tag>
struct Attachment {
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
static_assert(List_Value<Value>, "readable import batch value must satisfy List_Value");
struct Internal final : Concurrent_Hooks {
/* 仅同一内部线程可调用;返回借用不得保存到下一次 advance。 */
Value* use();
void advance();
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn);
Value buf[3]{}; /* 写入、内部消费、外部查询三个循环槽位。 */
Value* external_write{&buf[0]}; /* 外部线程写入面的非拥有借用。 */
Value* internal_read{&buf[1]}; /* 内部线程消费面的非拥有借用。 */
Value* external_read{&buf[2]}; /* 外部线程查询面的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护两个外部角色、角色轮换和复用槽清理。 */
};
void set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) internal.on_write_access();
std::invoke(std::forward<Fn>(fn), *internal.external_write);
}
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(*internal.external_read));
}
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
struct Internal final : Concurrent_Hooks {
Value* use() {
return internal_read;
}
const Value* use() const {
return internal_read;
}
void advance() {
std::lock_guard lock(exchange_mutex);
Value* const external_write_before = external_write;
Value* const internal_read_before = internal_read;
Value* const external_read_before = external_read;
static_assert(noexcept(this->before_advance(external_write_before, internal_read_before, external_read_before)));
this->before_advance(external_write_before, internal_read_before, external_read_before);
Value* const reuse = external_read;
external_read = internal_read;
internal_read = external_write;
external_write = reuse;
external_write->clear();
static_assert(noexcept(this->after_advance(external_write_before, internal_read_before, external_read_before)));
this->after_advance(external_write_before, internal_read_before, external_read_before);
}
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal_read);
}
Value buf[3]{}; /* 写入、内部消费、外部查询三个循环槽位。 */
Value* external_write{&buf[0]}; /* 外部线程写入面的非拥有借用。 */
Value* internal_read{&buf[1]}; /* 内部线程消费面的非拥有借用。 */
Value* external_read{&buf[2]}; /* 外部线程查询面的非拥有借用。 */
mutable std::mutex exchange_mutex; /* 保护两个外部角色、角色轮换和复用槽清理。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
};
} // namespace detail
template <typename... Tags>
struct Import_List_Concurrent : detail::Import_List_Concurrent_Definition<Import_List_Concurrent<Tags...>, detail::No_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Export_List_Concurrent : detail::Export_List_Concurrent_Definition<Export_List_Concurrent<Tags...>, detail::No_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Readable_Import_Batch_Concurrent : detail::Readable_Import_Batch_Concurrent_Definition<Readable_Import_Batch_Concurrent<Tags...>, detail::No_Concurrent_Hooks, Tags...> {};
} // namespace aethera
#include "List_Concurrent.ipp"
@@ -1,117 +0,0 @@
#pragma once
#include <functional>
#include <utility>
namespace aethera {
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Import_List_Concurrent<Value, Concurrent_Hooks>::set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) {
internal.on_write_access();
}
std::invoke(std::forward<Fn>(fn), *internal.external);
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Import_List_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal);
}
template <List_Value Value, typename Concurrent_Hooks>
Value* Import_List_Concurrent<Value, Concurrent_Hooks>::Internal::use() {
return this->internal;
}
template <List_Value Value, typename Concurrent_Hooks>
void Import_List_Concurrent<Value, Concurrent_Hooks>::Internal::advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = this->internal;
Value* const external_before = this->external;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
std::swap(this->internal, this->external);
this->external->clear();
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Reader<Value> Fn>
void Export_List_Concurrent<Value, Concurrent_Hooks>::get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(*internal.external));
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Export_List_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal);
}
template <List_Value Value, typename Concurrent_Hooks>
Value* Export_List_Concurrent<Value, Concurrent_Hooks>::Internal::use() {
if constexpr (requires { this->on_write_access(); }) {
this->on_write_access();
}
return this->internal;
}
template <List_Value Value, typename Concurrent_Hooks>
void Export_List_Concurrent<Value, Concurrent_Hooks>::Internal::advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = this->internal;
Value* const external_before = this->external;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
std::swap(this->internal, this->external);
this->internal->clear();
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Readable_Import_Batch_Concurrent<Value, Concurrent_Hooks>::set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) {
internal.on_write_access();
}
std::invoke(std::forward<Fn>(fn), *internal.external_write);
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Reader<Value> Fn>
void Readable_Import_Batch_Concurrent<Value, Concurrent_Hooks>::get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(*internal.external_read));
}
template <List_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Readable_Import_Batch_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), *internal_read);
}
template <List_Value Value, typename Concurrent_Hooks>
Value* Readable_Import_Batch_Concurrent<Value, Concurrent_Hooks>::Internal::use() {
return internal_read;
}
template <List_Value Value, typename Concurrent_Hooks>
void Readable_Import_Batch_Concurrent<Value, Concurrent_Hooks>::Internal::advance() {
std::lock_guard lock(exchange_mutex);
Value* const external_write_before = external_write;
Value* const internal_read_before = internal_read;
Value* const external_read_before = external_read;
static_assert(noexcept(this->before_advance(external_write_before, internal_read_before, external_read_before)));
this->before_advance(external_write_before, internal_read_before, external_read_before);
Value* const reuse = external_read;
external_read = internal_read;
internal_read = external_write;
external_write = reuse;
external_write->clear();
static_assert(noexcept(this->after_advance(external_write_before, internal_read_before, external_read_before)));
this->after_advance(external_write_before, internal_read_before, external_read_before);
} // namespace aethera
}
@@ -1,78 +1,141 @@
#pragma once
#include "../../../global.hpp"
#include "../../registration/Concurrent_Registration.hpp"
#include "../../detail/Concurrent_Definition.hpp"
#include <functional>
#include <mutex>
#include <utility>
namespace aethera {
/* 内部生产完整结构值,advance 后向外部发布一份稳定副本。 */
template <Exchange_Value Value, typename Concurrent_Hooks = detail::No_Concurrent_Hooks>
struct Export_Struct_Concurrent {
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const;
template <typename Owner, Copyable_Member Member> requires std::derived_from < Value
namespace detail {
/* 内部生产完整结构值,advance 后向外部发布稳定副本。 */
template <typename Definition, typename Concurrent_Hooks, typename... Tags>
struct Export_Struct_Concurrent_Definition : Concurrent_Definition<Definition, Tags...> {
template <typename Endpoint, typename Tag>
struct Attachment {
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
static_assert(Exchange_Value<Value>, "export struct value must satisfy Exchange_Value");
,
Owner
>
auto get(Member Owner::* member) const -> std::remove_cv_t<Member>;
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(internal.external_read));
}
template <typename Owner, Copyable_Member Member> requires std::derived_from<Value, Owner>
auto get(Member Owner::* member) const -> std::remove_cv_t<Member> {
std::lock_guard lock(internal.exchange_mutex);
return internal.external_read.*member;
}
struct Internal final : Concurrent_Hooks {
/* 仅同一内部线程可调用;可写借用会记录本轮修改,且不得跨越 advance。 */
Value* use();
void advance();
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn);
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from < Value
struct Internal final : Concurrent_Hooks {
Value* use() {
if constexpr (requires { this->on_write_access(); }) this->on_write_access();
return &internal_read;
}
const Value* use() const {
return &internal_read;
}
void advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = &internal_read;
Value* const external_before = &external_read;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
external_read = internal_read;
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), internal_read);
external_read = internal_read;
}
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void builder_set(Member Owner::* member, T&& value) {
internal_read.*member = std::forward<T>(value);
external_read = internal_read;
}
,
Owner
>
void builder_set(Member Owner::* member, T&& value);
Value internal_read{}; /* 内部线程生产面。 */
Value external_read{}; /* 部线程稳定读面。 */
mutable std::mutex exchange_mutex; /* 仅保护发布复制与外部读取。 */
Value internal_read{}; /* 内部线程生产面。 */
Value external_read{}; /* 外部线程稳定读面。 */
mutable std::mutex exchange_mutex; /* 仅保护发布复制与外部读取。 */
};
Internal internal{}; /* 部线程入口;use/advance 必须由同一线程调用。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
/* 外部编辑完整结构值,advance 后内部线程更新稳定副本。 */
template <Exchange_Value Value, typename Concurrent_Hooks = detail::No_Concurrent_Hooks>
struct Import_Struct_Concurrent {
template <Value_Writer<Value> Fn>
void set(Fn&& fn);
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from < Value
/* 外部编辑完整结构值,advance 后内部线程发布稳定副本。 */
template <typename Definition, typename Concurrent_Hooks, typename... Tags>
struct Import_Struct_Concurrent_Definition : Concurrent_Definition<Definition, Tags...> {
template <typename Endpoint, typename Tag>
struct Attachment {
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
static_assert(Exchange_Value<Value>, "import struct value must satisfy Exchange_Value");
,
Owner
>
void set(Member Owner::* member, T&& value);
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const;
template <typename Owner, Copyable_Member Member> requires std::derived_from < Value
,
Owner
>
auto get(Member Owner::* member) const -> std::remove_cv_t<Member>;
struct Internal final : Concurrent_Hooks {
/* 仅同一内部线程可调用;返回借用不得保存到下一次 advance。 */
Value* use();
void advance();
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn);
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from < Value
void set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) internal.on_write_access();
std::invoke(std::forward<Fn>(fn), internal.external_write);
}
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void set(Member Owner::* member, T&& value) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(internal.external_write, member); }) internal.on_write_access(internal.external_write, member);
else if constexpr (requires { internal.on_write_access(); }) internal.on_write_access();
internal.external_write.*member = std::forward<T>(value);
}
template <Value_Reader<Value> Fn>
void get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(internal.external_write));
}
template <typename Owner, Copyable_Member Member> requires std::derived_from<Value, Owner>
auto get(Member Owner::* member) const -> std::remove_cv_t<Member> {
std::lock_guard lock(internal.exchange_mutex);
return internal.external_write.*member;
}
,
Owner
>
void builder_set(Member Owner::* member, T&& value);
Value internal_read{}; /* 内部线程稳定读面。 */
Value external_write{}; /* 外部线程编辑面。 */
mutable std::mutex exchange_mutex; /* 仅保护外部编辑与内部推进复制。 */
struct Internal final : Concurrent_Hooks {
Value* use() {
return &internal_read;
}
const Value* use() const {
return &internal_read;
}
void advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = &internal_read;
Value* const external_before = &external_write;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
internal_read = external_write;
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Value_Writer<Value> Fn>
void builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), internal_read);
external_write = internal_read;
}
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void builder_set(Member Owner::* member, T&& value) {
internal_read.*member = std::forward<T>(value);
external_write = internal_read;
}
Value internal_read{}; /* 内部线程稳定读面。 */
Value external_write{}; /* 外部线程编辑面。 */
mutable std::mutex exchange_mutex; /* 仅保护外部编辑与内部推进复制。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
Internal internal{}; /* 内部线程入口;use/advance 必须由同一线程调用。 */
};
} // namespace detail
template <typename... Tags>
struct Export_Struct_Concurrent : detail::Export_Struct_Concurrent_Definition<Export_Struct_Concurrent<Tags...>, detail::No_Concurrent_Hooks, Tags...> {};
template <typename... Tags>
struct Import_Struct_Concurrent : detail::Import_Struct_Concurrent_Definition<Import_Struct_Concurrent<Tags...>, detail::No_Concurrent_Hooks, Tags...> {};
} // namespace aethera
#include "Struct_Concurrent.ipp"
@@ -1,120 +0,0 @@
#pragma once
#include <functional>
#include <utility>
namespace aethera {
template <Exchange_Value Value, typename Concurrent_Hooks>
template <Value_Reader<Value> Fn>
void Export_Struct_Concurrent<Value, Concurrent_Hooks>::get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(internal.external_read));
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <typename Owner, Copyable_Member Member> requires std::derived_from<Value, Owner>
auto Export_Struct_Concurrent<Value, Concurrent_Hooks>::get(Member Owner::* member) const -> std::remove_cv_t<Member> {
std::lock_guard lock(internal.exchange_mutex);
return internal.external_read.*member;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
Value* Export_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::use() {
if constexpr (requires { this->on_write_access(); }) {
this->on_write_access();
}
return &internal_read;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
void Export_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = &internal_read;
Value* const external_before = &external_read;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
external_read = internal_read;
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Export_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), internal_read);
external_read = internal_read;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void Export_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Member Owner::* member, T&& value) {
internal_read.*member = std::forward<T>(value);
external_read = internal_read;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::set(Fn&& fn) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(); }) {
internal.on_write_access();
}
std::invoke(std::forward<Fn>(fn), internal.external_write);
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::set(Member Owner::* member, T&& value) {
std::lock_guard lock(internal.exchange_mutex);
if constexpr (requires { internal.on_write_access(internal.external_write, member); }) {
internal.on_write_access(internal.external_write, member);
}
else if constexpr (requires { internal.on_write_access(); }) {
internal.on_write_access();
}
internal.external_write.*member = std::forward<T>(value);
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <Value_Reader<Value> Fn>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::get(Fn&& fn) const {
std::lock_guard lock(internal.exchange_mutex);
std::invoke(std::forward<Fn>(fn), std::as_const(internal.external_write));
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <typename Owner, Copyable_Member Member> requires std::derived_from<Value, Owner>
auto Import_Struct_Concurrent<Value, Concurrent_Hooks>::get(Member Owner::* member) const -> std::remove_cv_t<Member> {
std::lock_guard lock(internal.exchange_mutex);
return internal.external_write.*member;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
Value* Import_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::use() {
return &internal_read;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::advance() {
std::lock_guard lock(exchange_mutex);
Value* const internal_before = &internal_read;
Value* const external_before = &external_write;
static_assert(noexcept(this->before_advance(internal_before, external_before)));
this->before_advance(internal_before, external_before);
internal_read = external_write;
static_assert(noexcept(this->after_advance(internal_before, external_before)));
this->after_advance(internal_before, external_before);
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <Value_Writer<Value> Fn>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Fn&& fn) {
std::invoke(std::forward<Fn>(fn), internal_read);
external_write = internal_read;
}
template <Exchange_Value Value, typename Concurrent_Hooks>
template <typename Owner, Member_Value Member, Assignable_To<Member&> T> requires std::derived_from<Value, Owner>
void Import_Struct_Concurrent<Value, Concurrent_Hooks>::Internal::builder_set(Member Owner::* member, T&& value) {
internal_read.*member = std::forward<T>(value);
external_write = internal_read;
}
} // namespace aethera
+12 -11
View File
@@ -1,20 +1,19 @@
# Model 设计
`Model` 只负责定义层、注册项、附件物化和 CRTP API 组合,不认识并发结构、关系结构或任何具体容器。注册协议统一放在
`model/registration`,具体实现统一放在 `model/attachment`;新的能力族应在 `Concurrent_Registration`
`Relation_Registration` 的同级位置定义自己的注册协议,不得把协议分支加回 `Model`
`Model` 只负责定义层、附件物化和 CRTP API 组合,不认识并发结构、关系结构或任何具体容器。具体能力定义统一放在
`model/attachment`,公共组合协议放在 `model/detail`;新的能力族自行提供定义基类,不得把协议分支加回 `Model`
每个注册协议提供以下组成部分:
每个一段式附件定义提供以下组成部分:
- `Entries`:一个注册组展开得到的注册项列表。
- 注册项:定义 `Tag_Type``Protocol``Value<Endpoint>``Attachment<Endpoint>``Valid<Endpoint>`
- `Protocol`:分别定义 `Model_API<Endpoint, Base>``Builder_API<Endpoint, Base>``Private_API<Endpoint, Base>`
- concept:在最终 endpoint 物化时检查具体附件是否满足该协议。
`Def<Self, Base, Registrations...>` 累计各层注册项,对协议去重,然后选择性组合协议提供的 Model、Builder 和 Private CRTP
`Def<Self, Base, Definitions...>` 累计各层附件项,对协议去重,然后选择性组合协议提供的 Model、Builder 和 Private CRTP
API。没有注册某协议的最终类型不会继承该协议的函数。
实现按四步阅读即可:`Merge_Model_Type_Lists` 合并注册项,`Collect_Model_Protocols` 对协议去重,`Compose_Model_APIs`
实现按四步阅读即可:`Merge_Model_Type_Lists` 合并附件项,`Collect_Model_Protocols` 对协议去重,`Compose_Model_APIs`
复用同一递归组合三类 API`Endpoint_Attachments` 最后物化唯一附件集合。三类 API 不再各自维护一套递归模板。
业务值类型定义在所属类内并继承 `Prev<Tag>`。派生层只增量定义自己的字段,最终 endpoint 的值类型自然包含完整继承链。每层都是普通类并把自身传给
@@ -24,13 +23,15 @@ Builder 在构建具体 endpoint 时,才由公共 `Root` 一次物化该 endpo
的待发布对象是私有唯一所有权,只能通过 `build()` 移出。被继承的定义层只贡献注册、值类型和 Private 基类,不创建自己的状态。相同
Tag 重复注册属于编译期错误。
Root 的类型擦除 state 操作表保存一份 Private 解析器,Def 层不再保存 `d` 指针。框架内部通过零状态
Root 的类型擦除 state 操作表保存一份 Private 与附件解析器,Def 层不再保存 `d` 指针。框架内部通过零状态
`model_private(model)` 按模型的静态层选择类型化视图;模型转换为某个基类后,仍返回同一最终 Private 对象中的对应基类子对象。Model
实体必须通过 Builder 或 Model proxy 工厂物化;普通构造只建立定义对象,不产生运行状态。
实体必须通过 Builder 或 Model proxy 工厂物化;普通构造只建立定义对象,不产生运行状态。基础层 Private 调用
`concurrent<Tag>()` 时,Root 从最终 endpoint 的唯一附件解析实际值,再调整为该定义层的值基类视图;不得把基础 endpoint 的附件布局强转到最终 state。
跨模块 facade 直接声明消费方需要的 Private 能力。`make_model_proxy``make_model_proxy_shared``model_proxy_view`
分别承载独占、共享和非拥有生命周期,同时把 proxy 解引用目标映射到 Model 的唯一 Private;实体公开类不实现 facade 值转发。
并发结构使用 `Concurrent_Registration<Concrete, Tags...>` 注册,关系结构使用 `Relation_Registration<Concrete, Tags...>`
注册。一个具体实现可在同一注册组连续绑定多个 Tag。Builder 和实体只写 Tag,例如 `set<Prop_Tag>(...)`;具体附件类型由注册项和最终
endpoint 唯一确定。
具体类型自身就是一段式附件定义。例如,并发结构使用 `Import_Struct_Concurrent<Tags...>`,拥有型 DAG 使用
`Owned_Dag_Relation<Node_Facade>`。业务定义不再套额外的二段式注册包装。一个并发定义可连续绑定多个
Tag;它继承 `Concurrent_Definition<Self, Tags...>`,并在内部直接定义最终 endpoint 所物化的 `Attachment<Endpoint, Tag>`。Builder
和实体只写 Tag,例如 `set<Prop_Tag>(...)`;具体附件及其存储值由该定义和最终 endpoint 唯一确定。
@@ -0,0 +1,195 @@
#pragma once
#include "../Model.hpp"
#include <concepts>
#include <functional>
#include <utility>
namespace aethera {
namespace detail {
/* 用同一个最小回调检查 builder_set 与外部 set/get 是否真实存在。 */
struct Builder_Set_Probe {
template <typename Value>
void operator()(Value&) const;
};
/* 默认推进钩子不保存状态;参数是 advance 捕获的交换前角色。 */
struct No_Concurrent_Hooks {
template <typename... Values>
void before_advance(const Values*...) noexcept {}
template <typename... Values>
void after_advance(const Values*...) noexcept {}
};
} // namespace detail
template <typename Attachment>
concept Concurrent_Internal = requires(Attachment& attachment, const Attachment& const_attachment) {
attachment.internal.advance();
attachment.internal.use();
const_attachment.internal.use();
attachment.internal.builder_set(detail::Builder_Set_Probe{});
};
template <typename Attachment, typename... Args>
concept Concurrent_External_Set = requires(Attachment& attachment, Args&&... args) {
attachment.set(std::forward<Args>(args)...);
};
template <typename Attachment, typename... Args>
concept Concurrent_External_Get = requires(const Attachment& attachment, Args&&... args) {
attachment.get(std::forward<Args>(args)...);
};
template <typename Attachment>
concept Concurrent_Attachment = Concurrent_Internal<Attachment> && (Concurrent_External_Set<Attachment, detail::Builder_Set_Probe> || Concurrent_External_Get<Attachment, detail::Builder_Set_Probe>);
namespace detail {
template <typename Value, typename Fn> requires Value_Writer<Fn, Value>
void initialize_concurrent_value(Value& target, Fn&& fn) {
std::invoke(std::forward<Fn>(fn), target);
}
template <typename Value, typename Owner, typename Member, typename T> requires std::derived_from<Value, Owner> && std::assignable_from<Member&, T>
void initialize_concurrent_value(Value& target, Member Owner::* member, T&& value) {
target.*member = std::forward<T>(value);
}
template <typename Value, typename T> requires std::assignable_from<Value&, T>
void initialize_concurrent_value(Value& target, T&& value) {
target = std::forward<T>(value);
}
template <typename Value, typename... Args>
concept Concurrent_Value_Initializer = requires(Value& value, Args&&... args) {
initialize_concurrent_value(value, std::forward<Args>(args)...);
};
template <typename Endpoint, typename Base>
struct Concurrent_Model_API;
template <typename Endpoint, typename Base>
struct Concurrent_Builder_API;
template <typename Endpoint, typename Base>
struct Concurrent_Private_API;
template <typename Endpoint, typename Tag>
struct Concurrent_Private_View {
using Value = Materialized_Model_Value<Tag, Endpoint>;
struct Internal {
void advance() {
instance->advance_concurrent(&Model_Tag_Type_Token<Tag>);
}
Value* use() {
return static_cast<Value*>(instance->resolve_concurrent_value(&Model_Tag_Type_Token<Tag>, &Model_Private_Type_Token<Endpoint>));
}
const Value* use() const {
return static_cast<const Value*>(instance->resolve_const_concurrent_value(&Model_Tag_Type_Token<Tag>, &Model_Private_Type_Token<Endpoint>));
}
Model_Instance* instance; /* 非拥有借用;临时视图不超过当前 Private 调用。 */
};
template <typename... Args> requires Concurrent_Value_Initializer<Value, Args...>
void set(Args&&... args) {
auto operation = [&](Value& value) { initialize_concurrent_value(value, std::forward<Args>(args)...); };
const auto invoke = [](void* value, void* context) { (*static_cast<decltype(operation)*>(context))(*static_cast<Value*>(value)); };
instance->write_concurrent_value(&Model_Tag_Type_Token<Tag>, &Model_Private_Type_Token<Endpoint>, invoke, std::addressof(operation));
}
Model_Instance* instance; /* 非拥有借用;与 internal 指向同一个 Root endpoint state。 */
Internal internal{instance};
};
template <typename Endpoint, typename Tag>
struct Concurrent_Const_Private_View {
using Value = Materialized_Model_Value<Tag, Endpoint>;
struct Internal {
const Value* use() const {
return static_cast<const Value*>(instance->resolve_const_concurrent_value(&Model_Tag_Type_Token<Tag>, &Model_Private_Type_Token<Endpoint>));
}
const Model_Instance* instance; /* 非拥有借用;临时视图不超过当前 const Private 调用。 */
};
const Model_Instance* instance; /* 非拥有借用;只暴露内部稳定读面。 */
Internal internal{instance};
};
struct Concurrent_Protocol {
template <typename Endpoint, typename Base>
using Model_API = Concurrent_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Concurrent_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Concurrent_Private_API<Endpoint, Base>;
};
/* 具体并发定义负责 Attachment;公共协议只把定义与 Tag 接入 Model。 */
template <typename Definition, typename Tag>
struct Concurrent_Entry {
using Tag_Type = Tag;
using Protocol = typename Definition::Protocol;
template <typename Endpoint>
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
template <typename Endpoint>
using Attachment = typename Definition::template Attachment<Endpoint, Tag>;
template <typename Endpoint>
static constexpr bool Valid = Concurrent_Attachment<Attachment<Endpoint>>;
};
template <typename Definition, typename... Tags>
struct Concurrent_Definition {
static_assert(sizeof...(Tags) > 0, "a concurrent definition requires at least one tag");
using Protocol = Concurrent_Protocol;
template <typename Endpoint, typename Base>
using Model_API = Concurrent_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Concurrent_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Concurrent_Private_API<Endpoint, Base>;
using Entries = Model_Type_List<Concurrent_Entry<Definition, Tags>...>;
};
template <typename Tag, typename Endpoint>
concept Concurrent_Model_Tag = Model_Tag_Uses_Protocol<Tag, Endpoint, Concurrent_Protocol>;
template <typename Tag, typename Endpoint, typename... Args>
concept Builder_Settable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_Internal<Materialized_Model_Attachment<Tag, Endpoint>> && Concurrent_Value_Initializer<Materialized_Model_Value<Tag, Endpoint>, Args...>;
template <typename Tag, typename Endpoint, typename... Args>
concept Settable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_External_Set<Materialized_Model_Attachment<Tag, Endpoint>, Args...>;
template <typename Tag, typename Endpoint, typename... Args>
concept Gettable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_External_Get<Materialized_Model_Attachment<Tag, Endpoint>, Args...>;
template <typename Endpoint, typename Base>
struct Concurrent_Model_API : Base {
template <typename Tag, typename... Args> requires Settable_Concurrent<Tag, Endpoint, Args...>
Endpoint& set(Args&&... args) {
auto& self = static_cast<Endpoint&>(*this);
model_attachment<Tag>(self).set(std::forward<Args>(args)...);
return self;
}
template <typename Tag, typename... Args> requires Gettable_Concurrent<Tag, Endpoint, Args...>
const Endpoint& get(Args&&... args) const {
const auto& self = static_cast<const Endpoint&>(*this);
model_attachment<Tag>(self).get(std::forward<Args>(args)...);
return self;
}
};
template <typename Endpoint, typename Base>
struct Concurrent_Builder_API : Base {
template <typename Tag, typename... Args> requires Builder_Settable_Concurrent<Tag, Endpoint, Args...>
typename Endpoint::Builder& set(Args&&... args) {
auto& builder = static_cast<typename Endpoint::Builder&>(*this);
auto& concurrent = model_attachment<Tag>(this->model());
concurrent.internal.builder_set([&](auto& value) {
initialize_concurrent_value(value, std::forward<Args>(args)...);
});
return builder;
}
};
template <typename Endpoint, typename Base>
struct Concurrent_Private_API : Base {
template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
auto concurrent() noexcept {
return Concurrent_Private_View<Endpoint, Tag>{this->instance};
}
template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
auto concurrent() const noexcept {
return Concurrent_Const_Private_View<Endpoint, Tag>{this->instance};
}
};
} // namespace detail
} // namespace aethera
@@ -0,0 +1,111 @@
#pragma once
#include "../Model.hpp"
#include <concepts>
#include <utility>
namespace aethera {
namespace detail {
/* 只验证 Builder 初始化入口,不保存任何关系状态。 */
struct Relation_Build_Probe {
template <typename Relation>
void operator()(Relation&) const;
};
} // namespace detail
template <typename Attachment>
concept Relation_Attachment = requires(Attachment& attachment, typename Attachment::Graph graph, typename Attachment::Edit edit, typename Attachment::Edit_Completion completion) {
{ attachment.edit(std::move(graph)) } -> std::same_as<void>;
{ attachment.edit(std::move(edit), std::move(completion)) } -> std::same_as<void>;
{ attachment.internal.relation() } -> std::same_as<typename Attachment::Graph&>;
{ attachment.internal.commit() } -> std::same_as<void>;
{ attachment.internal.build(detail::Relation_Build_Probe{}) } -> std::same_as<void>;
};
namespace detail {
template <typename Endpoint, typename Base>
struct Relation_Model_API;
template <typename Endpoint, typename Base>
struct Relation_Builder_API;
template <typename Endpoint, typename Base>
struct Relation_Private_API;
struct Relation_Protocol {
template <typename Endpoint, typename Base>
using Model_API = Relation_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Relation_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Relation_Private_API<Endpoint, Base>;
};
/* 关系定义本身提供 Attachment;公共协议只负责把 Tag 能力组合到 endpoint。 */
template <typename Definition, typename Tag>
struct Relation_Entry {
using Tag_Type = Tag;
using Protocol = typename Definition::Protocol;
template <typename>
using Value = Tag;
template <typename Endpoint>
using Attachment = typename Definition::template Attachment<Endpoint, Tag>;
template <typename Endpoint>
static constexpr bool Valid = Relation_Attachment<Attachment<Endpoint>>;
};
template <typename Definition, typename... Tags>
struct Relation_Definition {
static_assert(sizeof...(Tags) > 0, "a relation definition requires at least one tag");
using Protocol = Relation_Protocol;
template <typename Endpoint, typename Base>
using Model_API = Relation_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Relation_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Relation_Private_API<Endpoint, Base>;
using Entries = Model_Type_List<Relation_Entry<Definition, Tags>...>;
};
template <typename Tag, typename Endpoint>
concept Relation_Model_Tag = Model_Tag_Uses_Protocol<Tag, Endpoint, Relation_Protocol>;
template <typename Tag, typename Endpoint, typename... Args>
concept Editable_Relation = Relation_Model_Tag<Tag, Endpoint> && requires(Materialized_Model_Attachment<Tag, Endpoint>& relation, Args&&... args) {
relation.edit(std::forward<Args>(args)...);
};
template <typename Tag, typename Endpoint, typename Fn>
concept Buildable_Relation = Relation_Model_Tag<Tag, Endpoint> && std::invocable<Fn, typename Materialized_Model_Attachment<Tag, Endpoint>::Graph&>;
template <typename Endpoint, typename Base>
struct Relation_Model_API : Base {
template <typename Tag, typename... Args> requires Editable_Relation<Tag, Endpoint, Args...>
Endpoint& edit(Args&&... args) {
auto& self = static_cast<Endpoint&>(*this);
model_private(self).template relation_attachment<Tag>().edit(std::forward<Args>(args)...);
return self;
}
};
template <typename Endpoint, typename Base>
struct Relation_Builder_API : Base {
template <typename Tag, typename Fn> requires Buildable_Relation<Tag, Endpoint, Fn>
typename Endpoint::Builder& relation(Fn&& fn) {
auto& builder = static_cast<typename Endpoint::Builder&>(*this);
model_private(this->model()).template relation_attachment<Tag>().internal.build(std::forward<Fn>(fn));
return builder;
}
};
template <typename Endpoint, typename Base>
struct Relation_Private_API : Base {
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& relation_attachment() noexcept {
return this->template attachment<Tag>();
}
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& relation() noexcept {
return this->template attachment<Tag>().internal.relation();
}
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
void commit() {
this->template attachment<Tag>().internal.commit();
}
};
} // namespace detail
} // namespace aethera
@@ -1,102 +0,0 @@
#pragma once
#include "../Model.hpp"
#include <concepts>
#include <functional>
#include <utility>
namespace aethera {
namespace detail {
/* 用同一个最小回调检查 builder_set 与外部 set/get 是否真实存在。 */
struct Builder_Set_Probe {
template <typename Value>
void operator()(Value&) const;
};
/* 默认推进钩子:不记录状态;参数是 advance 入口捕获的交换前角色。 */
struct No_Concurrent_Hooks {
template <typename... Values>
void before_advance(const Values*... before) noexcept;
template <typename... Values>
void after_advance(const Values*... before) noexcept;
};
} // namespace detail
template <typename Attachment>concept Concurrent_Internal = requires(Attachment& attachment) {
attachment.internal.advance(); attachment.internal.use(); attachment.internal.builder_set(detail::Builder_Set_Probe{});
};
template <typename Attachment, typename... Args>concept Concurrent_External_Set = requires(Attachment& attachment, Args&&... args) {
attachment.set(std::forward<Args>(args)...);
};
template <typename Attachment, typename... Args>concept Concurrent_External_Get = requires(const Attachment& attachment, Args&&... args) {
attachment.get(std::forward<Args>(args)...);
};
template <typename Attachment>concept Concurrent_Attachment = Concurrent_Internal<Attachment> && (Concurrent_External_Set<Attachment, detail::Builder_Set_Probe> || Concurrent_External_Get<Attachment, detail::Builder_Set_Probe>);
namespace detail {
/* Builder 支持回调、成员指针和值三种初始化形式,最终都归一为回调。 */
template <typename Value, typename Fn> requires Value_Writer<Fn, Value>
void initialize_concurrent_value(Value& target, Fn&& fn);
template <typename Value, typename Owner, typename Member, typename T> requires std::derived_from<Value, Owner> && std::assignable_from<Member&, T>
void initialize_concurrent_value(Value& target, Member Owner::* member, T&& value);
template <typename Value, typename T> requires std::assignable_from<Value&, T>
void initialize_concurrent_value(Value& target, T&& value);
template <typename Value, typename... Args>concept Concurrent_Value_Initializer = requires(Value& value, Args&&... args) { initialize_concurrent_value(value, std::forward<Args>(args)...); };
template <typename Endpoint, typename Base>
struct Concurrent_Model_API;
template <typename Endpoint, typename Base>
struct Concurrent_Builder_API;
template <typename Endpoint, typename Base>
struct Concurrent_Private_API;
struct Concurrent_Protocol {
template <typename Endpoint, typename Base>
using Model_API = Concurrent_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Concurrent_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Concurrent_Private_API<Endpoint, Base>;
};
template <template <typename...> typename Concurrent, typename Tag>
struct Concurrent_Entry {
using Tag_Type = Tag;
using Protocol = Concurrent_Protocol;
template <typename Endpoint>
using Value = typename Model_Tag_Value<Tag, Endpoint>::Type;
template <typename Endpoint>
using Attachment = Concurrent<Value<Endpoint>>;
template <typename Endpoint>
static constexpr bool Valid = Concurrent_Attachment<Attachment<Endpoint>>;
};
template <typename Tag, typename Endpoint>concept Concurrent_Model_Tag = Model_Tag_Uses_Protocol<Tag, Endpoint, Concurrent_Protocol>;
template <typename Tag, typename Endpoint, typename... Args>concept Builder_Settable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_Internal<Registered_Model_Attachment<Tag, Endpoint>> && Concurrent_Value_Initializer<Registered_Model_Value<Tag, Endpoint>, Args...>;
template <typename Tag, typename Endpoint, typename... Args>concept Settable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_External_Set<Registered_Model_Attachment<Tag, Endpoint>, Args...>;
template <typename Tag, typename Endpoint, typename... Args>concept Gettable_Concurrent = Concurrent_Model_Tag<Tag, Endpoint> && Concurrent_External_Get<Registered_Model_Attachment<Tag, Endpoint>, Args...>;
template <typename Endpoint, typename Base>
struct Concurrent_Model_API : Base {
template <typename Tag, typename... Args> requires Settable_Concurrent<Tag, Endpoint, Args...>
Endpoint& set(Args&&... args);
template <typename Tag, typename... Args> requires Gettable_Concurrent<Tag, Endpoint, Args...>
const Endpoint& get(Args&&... args) const;
};
template <typename Endpoint, typename Base>
struct Concurrent_Builder_API : Base {
template <typename Tag, typename... Args> requires Builder_Settable_Concurrent<Tag, Endpoint, Args...>
typename Endpoint::Builder& set(Args&&... args);
};
template <typename Endpoint, typename Base>
struct Concurrent_Private_API : Base {
template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
auto& concurrent() noexcept;
template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
const auto& concurrent() const noexcept;
};
} // namespace detail
/* 同一 Concurrent 模板可连续注册多个 Tag;每个 Tag 在最终 endpoint 上独立求值。 */
template <template <typename...> typename Concurrent, typename... Tags> requires (sizeof...(Tags) > 0)
struct Concurrent_Registration {
using Entries = detail::Model_Type_List<detail::Concurrent_Entry<Concurrent, Tags>...>;
};
} // namespace aethera
#include "Concurrent_Registration.ipp"
@@ -1,46 +0,0 @@
#pragma once
namespace aethera::detail {
template <typename... Values> void No_Concurrent_Hooks::before_advance(const Values*...) noexcept {}
template <typename... Values> void No_Concurrent_Hooks::after_advance(const Values*...) noexcept {}
template <typename Value, typename Fn> requires Value_Writer<Fn, Value>
void initialize_concurrent_value(Value& target, Fn&& fn) {
std::invoke(std::forward<Fn>(fn), target);
}
template <typename Value, typename Owner, typename Member, typename T> requires std::derived_from<Value, Owner> && std::assignable_from<Member&, T>
void initialize_concurrent_value(Value& target, Member Owner::* member, T&& value) {
target.*member = std::forward<T>(value);
}
template <typename Value, typename T> requires std::assignable_from<Value&, T>
void initialize_concurrent_value(Value& target, T&& value) {
target = std::forward<T>(value);
}
template <typename Endpoint, typename Base> template <typename Tag, typename... Args> requires Settable_Concurrent<Tag, Endpoint, Args...>
Endpoint& Concurrent_Model_API<Endpoint, Base>::set(Args&&... args) {
auto& self = static_cast<Endpoint&>(*this);
model_private(self).template concurrent<Tag>().set(std::forward<Args>(args)...);
return self;
}
template <typename Endpoint, typename Base> template <typename Tag, typename... Args> requires Gettable_Concurrent<Tag, Endpoint, Args...>
const Endpoint& Concurrent_Model_API<Endpoint, Base>::get(Args&&... args) const {
const auto& self = static_cast<const Endpoint&>(*this);
model_private(self).template concurrent<Tag>().get(std::forward<Args>(args)...);
return self;
}
template <typename Endpoint, typename Base> template <typename Tag, typename... Args> requires Builder_Settable_Concurrent<Tag, Endpoint, Args...>
typename Endpoint::Builder& Concurrent_Builder_API<Endpoint, Base>::set(Args&&... args) {
auto& builder = static_cast<typename Endpoint::Builder&>(*this);
auto& concurrent = model_private(this->model()).template concurrent<Tag>();
concurrent.internal.builder_set([&](auto& value) {
initialize_concurrent_value(value, std::forward<Args>(args)...);
});
return builder;
}
template <typename Endpoint, typename Base> template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
auto& Concurrent_Private_API<Endpoint, Base>::concurrent() noexcept {
return this->template attachment<Tag>();
}
template <typename Endpoint, typename Base> template <typename Tag> requires Concurrent_Model_Tag<Tag, Endpoint>
const auto& Concurrent_Private_API<Endpoint, Base>::concurrent() const noexcept {
return this->template attachment<Tag>();
}
} // namespace aethera::detail
@@ -1,78 +0,0 @@
#pragma once
#include "../Model.hpp"
#include <concepts>
#include <utility>
namespace aethera {
namespace detail {
/* 只验证 Builder 初始化入口,不保存任何关系状态。 */
struct Relation_Build_Probe {
template <typename Relation>
void operator()(Relation&) const;
};
} // namespace detail
template <typename Attachment>concept Relation_Attachment = requires(Attachment& attachment, typename Attachment::Graph graph, typename Attachment::Edit edit, typename Attachment::Edit_Completion completion) {
{ attachment.edit(std::move(graph)) } -> std::same_as<void>; { attachment.edit(std::move(edit), std::move(completion)) } -> std::same_as<void>; { attachment.internal.relation() } -> std::same_as<typename Attachment::Graph&>; { attachment.internal.commit() } -> std::same_as<void>; { attachment.internal.build(detail::Relation_Build_Probe{}) } -> std::same_as<void>;
};
namespace detail {
template <typename Endpoint, typename Base>
struct Relation_Model_API;
template <typename Endpoint, typename Base>
struct Relation_Builder_API;
template <typename Endpoint, typename Base>
struct Relation_Private_API;
struct Relation_Protocol {
template <typename Endpoint, typename Base>
using Model_API = Relation_Model_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Builder_API = Relation_Builder_API<Endpoint, Base>;
template <typename Endpoint, typename Base>
using Private_API = Relation_Private_API<Endpoint, Base>;
};
template <template <typename...> typename Relation, typename Tag>
struct Relation_Entry {
using Tag_Type = Tag;
using Protocol = Relation_Protocol;
template <typename>
using Value = Tag;
template <typename>
using Attachment = Relation<Tag>;
template <typename Endpoint>
static constexpr bool Valid = Relation_Attachment<Attachment<Endpoint>>;
};
template <typename Tag, typename Endpoint>concept Relation_Model_Tag = Model_Tag_Uses_Protocol<Tag, Endpoint, Relation_Protocol>;
template <typename Tag, typename Endpoint, typename... Args>concept Editable_Relation = Relation_Model_Tag<Tag, Endpoint> && requires(Registered_Model_Attachment<Tag, Endpoint>& relation, Args&&... args) { relation.edit(std::forward<Args>(args)...); };
template <typename Tag, typename Endpoint, typename Fn>concept Buildable_Relation = Relation_Model_Tag<Tag, Endpoint> && std::invocable<Fn, typename Registered_Model_Attachment<Tag, Endpoint>::Graph&>;
template <typename Endpoint, typename Base>
struct Relation_Model_API : Base {
template <typename Tag, typename... Args> requires Editable_Relation<Tag, Endpoint, Args...>
Endpoint& edit(Args&&... args);
};
template <typename Endpoint, typename Base>
struct Relation_Builder_API : Base {
template <typename Tag, typename Fn> requires Buildable_Relation<Tag, Endpoint, Fn>
typename Endpoint::Builder& relation(Fn&& fn);
};
template <typename Endpoint, typename Base>
struct Relation_Private_API : Base {
/* relation_attachment 仅供实体 edit 与 Builder build 进入对应附件。 */
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& relation_attachment() noexcept;
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& relation() noexcept;
template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
void commit();
};
} // namespace detail
/* 同一 Relation 模板可连续注册多个 Tag;Tag 同时是该关系实现的节点/值参数。 */
template <template <typename...> typename Relation, typename... Tags> requires (sizeof...(Tags) > 0)
struct Relation_Registration {
using Entries = detail::Model_Type_List<detail::Relation_Entry<Relation, Tags>...>;
};
} // namespace aethera
#include "Relation_Registration.ipp"
@@ -1,27 +0,0 @@
#pragma once
namespace aethera::detail {
template <typename Endpoint, typename Base> template <typename Tag, typename... Args> requires Editable_Relation<Tag, Endpoint, Args...>
Endpoint& Relation_Model_API<Endpoint, Base>::edit(Args&&... args) {
auto& self = static_cast<Endpoint&>(*this);
model_private(self).template relation_attachment<Tag>().edit(std::forward<Args>(args)...);
return self;
}
template <typename Endpoint, typename Base> template <typename Tag, typename Fn> requires Buildable_Relation<Tag, Endpoint, Fn>
typename Endpoint::Builder& Relation_Builder_API<Endpoint, Base>::relation(Fn&& fn) {
auto& builder = static_cast<typename Endpoint::Builder&>(*this);
model_private(this->model()).template relation_attachment<Tag>().internal.build(std::forward<Fn>(fn));
return builder;
}
template <typename Endpoint, typename Base> template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& Relation_Private_API<Endpoint, Base>::relation_attachment() noexcept {
return this->template attachment<Tag>();
}
template <typename Endpoint, typename Base> template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
auto& Relation_Private_API<Endpoint, Base>::relation() noexcept {
return this->template attachment<Tag>().internal.relation();
}
template <typename Endpoint, typename Base> template <typename Tag> requires Relation_Model_Tag<Tag, Endpoint>
void Relation_Private_API<Endpoint, Base>::commit() {
this->template attachment<Tag>().internal.commit();
}
} // namespace aethera::detail
@@ -1,7 +1,6 @@
#pragma once
#include "../export/export.h"
#include "scene/export/export.h"
#include "model/registration/Concurrent_Registration.hpp"
#include "model/attachment/dirty/Dirty_Concurrent.hpp"
#include "model/Model.hpp"
#include "statistics/Sliding_Statistics.hpp"
@@ -11,7 +10,7 @@
#include <expected>
#include <optional>
namespace aethera {
struct Frame_Policy : Def<Frame_Policy, Root, Concurrent_Registration<Dirty_Import_Struct_Concurrent, Prop_Tag>, Concurrent_Registration<Export_Struct_Concurrent, State_Tag>> {
struct Frame_Policy : Def<Frame_Policy, Root, Dirty_Import_Struct_Concurrent<Prop_Tag>, Export_Struct_Concurrent<State_Tag>> {
using Configuration_Result = Frame_Policy_Configuration_Result;
static constexpr double Default_Frames_Per_Second{30.0};
struct Prop : Prev<Prop_Tag> {
@@ -69,7 +69,7 @@ auto Throttled_Latest_only::Private::prepare_start() -> std::expected<std::chron
if (phase != Phase::starting) {
std::terminate();
}
auto& properties_storage = concurrent<Prop_Tag>();
auto properties_storage = concurrent<Prop_Tag>();
properties_storage.internal.advance();
const Prop& properties = *properties_storage.internal.use();
auto& state = *concurrent<State_Tag>().internal.use();
@@ -178,7 +178,7 @@ void Throttled_Latest_only::Private::frame_due() {
if (phase != Phase::running || !timer_id) {
return;
}
auto& properties_storage = concurrent<Prop_Tag>();
auto properties_storage = concurrent<Prop_Tag>();
properties_storage.internal.advance();
const Prop& properties = *properties_storage.internal.use();
auto& state = *concurrent<State_Tag>().internal.use();
@@ -1,7 +1,12 @@
#pragma once
#include "event/Event.hpp"
#include <optional>
namespace aethera {
struct Renderable::Private : Prev_Private {
template <typename Self> void advance(this Self& self);
virtual ~Private();
virtual void advance();
[[nodiscard]] virtual std::optional<double> event_routing_distance(const Event& event) const noexcept;
virtual void dispatch_event(Event& event);
proxy<task_flow::Task_Graph_Component> taskflow();
proxy<renderable::Task_Graph>& internal_taskflow() noexcept {
return graph;
@@ -9,8 +14,12 @@ struct Renderable::Private : Prev_Private {
private:
proxy<renderable::Task_Graph> graph{renderable::make_task_graph("renderable")}; /* 唯一拥有本 Renderable 的任务图代理。 */
};
template <typename Self> void Renderable::Private::advance(this Self& self) {
if constexpr (requires { self.advance_renderable(); }) self.advance_renderable();
inline Renderable::Private::~Private() = default;
inline void Renderable::Private::advance() {}
inline std::optional<double> Renderable::Private::event_routing_distance(const Event&) const noexcept {
return std::nullopt;
}
inline void Renderable::Private::dispatch_event(Event&) {
}
inline proxy<task_flow::Task_Graph_Component> Renderable::Private::taskflow() {
return graph->component();
@@ -4,7 +4,7 @@
namespace {
struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable> {
struct Private : Prev_Private {
void advance_renderable() {
void advance() override {
++advance_count;
auto& graph = internal_taskflow();
graph->clear();
@@ -14,6 +14,12 @@ enum struct Render_State_Result : std::uint8_t {
frame_missing,
completion_missing
};
enum struct Submit_Event_Result : std::uint8_t {
submitted,
/* Scene 已取得事件对象的唯一所有权。 */
event_missing
/* 未提供事件对象。 */
};
using Render_Error = std::variant<Render_State_Result, Compose_Task_Graph_Result, Precede_Task_Graph_Result, Run_Taskflow_Result>;
using Render_Result = std::expected<void, Render_Error>;
} // namespace aethera::scene
+8 -1
View File
@@ -1,9 +1,16 @@
#pragma once
#include "global.hpp"
#include "event/Event.hpp"
#include "task_flow/export/export.h"
#include <proxy/proxy.h>
namespace aethera::scene {
using Task_Graph = task_flow::Task_Graph_Compose_Facade;
proxy<Task_Graph> make_task_graph(std::string name);
struct Renderable : facade_builder::add_convention<_advance, void()>::add_convention<_taskflow, proxy<task_flow::Task_Graph_Component>()>::support_relocation<pro::constraint_level::nothrow>::build {};
struct Renderable : facade_builder
::add_convention<_advance, void()>
::add_convention<_taskflow, proxy<task_flow::Task_Graph_Component>()>
::add_convention<_event_routing_distance, std::optional<double>(const Event&) const noexcept>
::add_convention<_dispatch_event, void(Event &)>
::support_relocation<pro::constraint_level::nothrow>
::build {};
} // namespace aethera::scene
@@ -0,0 +1,25 @@
#include "Event_Collector.hpp"
#include "Error_handling_specification/Failure_Policy.hpp"
#include <concurrentqueue-1.0.5/concurrentqueue.h>
#include <new>
#include <utility>
namespace aethera::detail {
struct Event_Collector::Private {
moodycamel::ConcurrentQueue<std::unique_ptr<Event>> queue{}; /* 多生产者提交、Scene advance 单消费者取出的权威事件队列。 */
};
Event_Collector::Event_Collector() : d(std::make_unique<Private>()) {}
Event_Collector::~Event_Collector() = default;
void Event_Collector::submit(std::unique_ptr<Event> event) {
if (!d->queue.enqueue(std::move(event))) Failure_Policy::handle_unknown_failure(std::make_exception_ptr(std::bad_alloc{}));
}
Scene_Event_Batch Event_Collector::take() {
Scene_Event_Batch result;
std::unique_ptr<Event> event;
while (d->queue.try_dequeue(event)) result.push_back(std::move(event));
return result;
}
} // namespace aethera::detail
@@ -0,0 +1,22 @@
#pragma once
#include "event/Event.hpp"
#include <memory>
#include <vector>
namespace aethera::detail {
using Scene_Event_Batch = std::vector<std::unique_ptr<Event>>;
/* Scene 的 MPMC 输入边界;生产者只提交,唯一 advance 消费者负责取走并分发。 */
struct Event_Collector final {
Event_Collector();
~Event_Collector();
Event_Collector(const Event_Collector&) = delete;
Event_Collector& operator=(const Event_Collector&) = delete;
Event_Collector(Event_Collector&&) = delete;
Event_Collector& operator=(Event_Collector&&) = delete;
void submit(std::unique_ptr<Event> event);
Scene_Event_Batch take();
private:
struct Private;
std::unique_ptr<Private> d; /* 唯一拥有 concurrentqueue;不得与并发 submit 同时析构。 */
};
} // namespace aethera::detail
+18
View File
@@ -11,6 +11,7 @@ proxy<scene::Task_Graph> scene::make_task_graph(std::string name) {
return task_flow::make_task_graph(std::move(name));
}
Scene::Private::Private() = default;
Scene::Private::~Private() = default;
proxy<scene::Frame> Scene::Private::create_frame() {
return pro::make_proxy<scene::Frame, detail::Scene_Frame>();
}
@@ -49,8 +50,22 @@ auto Scene::Private::advance() -> std::expected<void, Create_Taskflow_Error> {
auto next_graph = create_taskflow();
if (!next_graph) return std::unexpected(next_graph.error());
graph = std::move(*next_graph);
dispatch_collected_events();
return {};
}
scene::Submit_Event_Result Scene::Private::submit_event(std::unique_ptr<Event> event) {
if (!event) return scene::Submit_Event_Result::event_missing;
event_collector.submit(std::move(event));
return scene::Submit_Event_Result::submitted;
}
void Scene::Private::dispatch_collected_events() {
auto events = event_collector.take();
if (!events.empty()) dispatch_events(events);
}
void Scene::Private::dispatch_events(Event_Batch& events) {
auto& renderables = relation<scene::Renderable>();
detail::dispatch_scene_events<scene::Renderable>(events, renderables);
}
scene::Render_Result Scene::Private::render(proxy<scene::Frame>& frame, scene::Render_Completion completion) {
if (!frame) return std::unexpected(scene::Render_Error{scene::Render_State_Result::frame_missing});
if (!completion) return std::unexpected(scene::Render_Error{scene::Render_State_Result::completion_missing});
@@ -67,4 +82,7 @@ scene::Render_Result Scene::Private::render(proxy<scene::Frame>& frame, scene::R
}
Scene::Scene() = default;
Scene::~Scene() noexcept = default;
scene::Submit_Event_Result Scene::submit_event(std::unique_ptr<Event> event) {
return detail::model_private(*this).submit_event(std::move(event));
}
} // namespace aethera
+4 -2
View File
@@ -1,17 +1,19 @@
#pragma once
#include "../export/export.h"
#include "event/Event.hpp"
#include "model/Model.hpp"
#include "model/attachment/dag/Dag_Relation.hpp"
#include "model/registration/Relation_Registration.hpp"
#include <functional>
#include <proxy/proxy.h>
#include "../rely_facade.h"
namespace aethera {
/* Scene 通过 Tag + Owned_Dag_Relation 在类型系统中表达其对 Renderable DAG 的唯一所有权。 */
struct Scene : Def<Scene, Root, Relation_Registration<Owned_Dag_Relation, scene::Renderable>> {
struct Scene : Def<Scene, Root, Owned_Dag_Relation<scene::Renderable>> {
struct Private;
Scene();
~Scene() noexcept;
/* 并发提交并转移事件唯一所有权;事件只在后续 advance 完成后分发。 */
scene::Submit_Event_Result submit_event(std::unique_ptr<Event> event);
};
} // namespace aethera
#include "Scene.ipp"
+48 -5
View File
@@ -1,15 +1,58 @@
#pragma once
#include "Event_Collector.hpp"
#include <algorithm>
#include <functional>
#include <optional>
#include <variant>
#include <vector>
namespace aethera {
struct Scene::Private : Prev_Private {
using Create_Taskflow_Error = std::variant<Compose_Task_Graph_Result, Precede_Task_Graph_Result>;
using Event_Batch = detail::Scene_Event_Batch;
Private();
proxy<scene::Frame> create_frame();
scene::Render_Result render(proxy<scene::Frame>& frame, scene::Render_Completion completion);
proxy<scene::Task_Graph>& taskflow();
auto create_taskflow() -> std::expected<proxy<scene::Task_Graph>, Create_Taskflow_Error>;
auto advance() -> std::expected<void, Create_Taskflow_Error>;
virtual ~Private();
proxy<scene::Frame> create_frame();
scene::Render_Result render(proxy<scene::Frame>& frame, scene::Render_Completion completion);
proxy<scene::Task_Graph>& taskflow();
auto create_taskflow() -> std::expected<proxy<scene::Task_Graph>, Create_Taskflow_Error>;
virtual auto advance() -> std::expected<void, Create_Taskflow_Error>;
virtual scene::Submit_Event_Result submit_event(std::unique_ptr<Event> event);
protected:
void dispatch_collected_events();
virtual void dispatch_events(Event_Batch& events);
private:
detail::Event_Collector event_collector{}; /* 唯一拥有所有生产者提交但尚未分发的事件。 */
proxy<scene::Task_Graph> graph{scene::make_task_graph("scene")}; /* 唯一拥有当前组合任务图的跨模块代理。 */
};
namespace detail {
template <typename Renderable>
struct Scene_Event_Target {
std::reference_wrapper<proxy<Renderable>> renderable; /* DAG 中 Renderable proxy 的非拥有借用。 */
std::optional<double> distance{}; /* 当前事件的路由距离;空值保持绘制层级顺序。 */
};
template <typename Renderable, typename Relation>
void dispatch_scene_events(Scene::Private::Event_Batch& events, Relation& renderables) {
std::vector<std::reference_wrapper<proxy<Renderable>>> paint_order;
renderables.for_each_topological([&](Dag_Node_Id, proxy<Renderable>& renderable) {
paint_order.emplace_back(renderable);
});
std::reverse(paint_order.begin(), paint_order.end());
for (auto& event : events) {
std::vector<Scene_Event_Target<Renderable>> delivery_order;
delivery_order.reserve(paint_order.size());
for (auto renderable : paint_order) delivery_order.push_back({renderable, renderable.get()->event_routing_distance(*event)});
std::stable_sort(delivery_order.begin(), delivery_order.end(), [](const auto& left, const auto& right) {
if (!left.distance) return right.distance.has_value();
if (!right.distance) return false;
return *left.distance < *right.distance;
});
for (auto& target : delivery_order) {
if (event->is_accepted()) break;
target.renderable.get()->dispatch_event(*event);
}
}
}
} // namespace detail
} // namespace aethera
@@ -8,7 +8,7 @@
namespace {
struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable> {
struct Private : Prev_Private {
void advance_renderable() {
void advance() override {
auto& graph = internal_taskflow();
graph->clear();
(void)graph->add("test.prepare", [] {});
@@ -18,7 +18,7 @@ struct Test_Renderable : aethera::Def<Test_Renderable, aethera::Renderable> {
struct Test_Dag_Renderable : aethera::Def<Test_Dag_Renderable, aethera::Renderable> {
struct Private : Prev_Private {
Private(std::shared_ptr<std::vector<int>> advance_order, int id) : advance_order(std::move(advance_order)), id(id) {}
void advance_renderable() {
void advance() override {
advance_order->push_back(id);
auto& graph = internal_taskflow();
graph->clear();
@@ -28,6 +28,29 @@ struct Test_Dag_Renderable : aethera::Def<Test_Dag_Renderable, aethera::Renderab
int id{};
};
};
struct Test_Event_Renderable : aethera::Def<Test_Event_Renderable, aethera::Renderable> {
struct Private : Prev_Private {
Private(std::shared_ptr<std::vector<int>> dispatch_order, std::shared_ptr<std::size_t> advances_seen, int id, double distance, bool accepts) : dispatch_order(std::move(dispatch_order)), advances_seen(std::move(advances_seen)), id(id), distance(distance), accepts(accepts) {}
void advance() override {
++*advances_seen;
auto& graph = internal_taskflow();
graph->clear();
(void)graph->add("event.prepare", [] {});
}
[[nodiscard]] std::optional<double> event_routing_distance(const aethera::Event&) const noexcept override {
return distance;
}
void dispatch_event(aethera::Event& event) override {
dispatch_order->push_back(id);
if (accepts) event.accept();
}
std::shared_ptr<std::vector<int>> dispatch_order; /* 测试观察的事件分发次序。 */
std::shared_ptr<std::size_t> advances_seen; /* 测试观察的 Renderable advance 次数。 */
int id{}; /* 当前测试 Renderable 标识。 */
double distance{}; /* 当前事件的路由距离。 */
bool accepts{}; /* 是否在收到事件后终止分发链。 */
};
};
TEST(Scene, Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) {
auto advance_order = std::make_shared<std::vector<int>>();
aethera::Dag_Relation<aethera::scene::Renderable>::Builder builder;
@@ -126,4 +149,23 @@ TEST(Scene, Rejects_The_Same_Renderable_Task_Graph_Twice) {
ASSERT_FALSE(rendered);
EXPECT_EQ(std::get<aethera::Compose_Task_Graph_Result>(rendered.error()), aethera::Compose_Task_Graph_Result::already_composed);
}
TEST(Scene, Collects_Events_And_Dispatches_After_Renderable_Advance) {
auto dispatch_order = std::make_shared<std::vector<int>>();
auto advances_seen = std::make_shared<std::size_t>();
aethera::Dag_Relation<aethera::scene::Renderable>::Builder builder;
builder.add({1}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Event_Renderable>(dispatch_order, advances_seen, 1, 10.0, false));
builder.add({2}, aethera::make_model_proxy<aethera::scene::Renderable, Test_Event_Renderable>(dispatch_order, advances_seen, 2, 1.0, true));
auto graph = builder.build();
ASSERT_TRUE(graph.has_value());
auto scene = aethera::Scene::Builder{}.build();
scene->edit<aethera::scene::Renderable>(std::move(*graph));
EXPECT_EQ(scene->submit_event(std::make_unique<aethera::Event>(aethera::Event_Type::show)), aethera::scene::Submit_Event_Result::submitted);
EXPECT_EQ(scene->submit_event({}), aethera::scene::Submit_Event_Result::event_missing);
auto& scene_private = aethera::detail::model_private(*scene);
ASSERT_TRUE(scene_private.advance().has_value());
EXPECT_EQ(*advances_seen, 2U);
EXPECT_EQ(*dispatch_order, (std::vector<int>{2}));
ASSERT_TRUE(scene_private.advance().has_value());
EXPECT_EQ(*dispatch_order, (std::vector<int>{2}));
}
} // namespace
+44 -16
View File
@@ -1,4 +1,3 @@
#include "model/registration/Concurrent_Registration.hpp"
#include "model/attachment/dirty/Dirty_Concurrent.hpp"
#include "model/attachment/list/List_Concurrent.hpp"
#include "model/attachment/structure/Struct_Concurrent.hpp"
@@ -28,7 +27,7 @@ struct Other_Number_List_Tag {
struct Middle_Data_Tag {
template <typename Layer> using Value = typename Layer::Middle_Data;
};
struct Test_Model_Base : aethera::Def<Test_Model_Base, aethera::Root, aethera::Concurrent_Registration<aethera::Dirty_Import_Struct_Concurrent, aethera::Prop_Tag>, aethera::Concurrent_Registration<aethera::Export_Struct_Concurrent, aethera::State_Tag>, aethera::Concurrent_Registration<aethera::Import_List_Concurrent, Number_List_Tag, Other_Number_List_Tag>> {
struct Test_Model_Base : aethera::Def<Test_Model_Base, aethera::Root, aethera::Dirty_Import_Struct_Concurrent<aethera::Prop_Tag>, aethera::Export_Struct_Concurrent<aethera::State_Tag>, aethera::Import_List_Concurrent<Number_List_Tag, Other_Number_List_Tag>> {
struct Prop : Prev<aethera::Prop_Tag> {
int base_number{};
int same_name{};
@@ -41,9 +40,15 @@ struct Test_Model_Base : aethera::Def<Test_Model_Base, aethera::Root, aethera::C
struct Private : Prev_Private {
Private() { ++test_model_base_private_instances; }
~Private() { --test_model_base_private_instances; }
void advance_properties() {
concurrent<aethera::Prop_Tag>().internal.advance();
}
void set_base_number(int value) {
concurrent<aethera::Prop_Tag>().set(&Prop::base_number, value);
}
};
};
struct Test_Model_Middle : aethera::Def<Test_Model_Middle, Test_Model_Base, aethera::Concurrent_Registration<aethera::Import_Struct_Concurrent, Middle_Data_Tag>> {
struct Test_Model_Middle : aethera::Def<Test_Model_Middle, Test_Model_Base, aethera::Import_Struct_Concurrent<Middle_Data_Tag>> {
struct Prop : Prev<aethera::Prop_Tag> {
int middle_number{};
};
@@ -99,11 +104,17 @@ static_assert(std::derived_from<Test_Model::Prop, Test_Model_Middle::Prop>);
static_assert(std::derived_from<Test_Model_Middle::State, Test_Model_Base::State>);
static_assert(std::derived_from<Test_Model::State, Test_Model_Middle::State>);
static_assert(std::derived_from<Test_Model::Numbers, Test_Model_Middle::Numbers>);
static_assert(aethera::Concurrent_Attachment<aethera::Dirty_Import_Struct_Concurrent<Test_Model::Prop>>);
static_assert(aethera::Concurrent_Attachment<aethera::Export_Struct_Concurrent<Test_Model::State>>);
static_assert(aethera::Concurrent_Attachment<aethera::Import_List_Concurrent<Test_Model::Numbers>>);
static_assert(aethera::Concurrent_Attachment<aethera::Import_List_Concurrent<Test_Model::Other_Numbers>>);
static_assert(aethera::Concurrent_Attachment<aethera::Import_Struct_Concurrent<Test_Model::Middle_Data>>);
static_assert(aethera::detail::Model_Attachment_Definition<aethera::Dirty_Import_Struct_Concurrent<aethera::Prop_Tag>>);
static_assert(aethera::detail::Model_Attachment_Definition<aethera::Import_List_Concurrent<Number_List_Tag, Other_Number_List_Tag>>);
static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<aethera::Prop_Tag, Test_Model>, aethera::Dirty_Import_Struct_Concurrent<aethera::Prop_Tag>::Attachment<Test_Model, aethera::Prop_Tag>>);
static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<Number_List_Tag, Test_Model>, aethera::Import_List_Concurrent<Number_List_Tag, Other_Number_List_Tag>::Attachment<Test_Model, Number_List_Tag>>);
static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<Other_Number_List_Tag, Test_Model>, aethera::Import_List_Concurrent<Number_List_Tag, Other_Number_List_Tag>::Attachment<Test_Model, Other_Number_List_Tag>>);
static_assert(!std::same_as<aethera::detail::Materialized_Model_Attachment<Number_List_Tag, Test_Model>, aethera::detail::Materialized_Model_Attachment<Other_Number_List_Tag, Test_Model>>);
static_assert(aethera::Concurrent_Attachment<aethera::detail::Materialized_Model_Attachment<aethera::Prop_Tag, Test_Model>>);
static_assert(aethera::Concurrent_Attachment<aethera::detail::Materialized_Model_Attachment<aethera::State_Tag, Test_Model>>);
static_assert(aethera::Concurrent_Attachment<aethera::detail::Materialized_Model_Attachment<Number_List_Tag, Test_Model>>);
static_assert(aethera::Concurrent_Attachment<aethera::detail::Materialized_Model_Attachment<Other_Number_List_Tag, Test_Model>>);
static_assert(aethera::Concurrent_Attachment<aethera::detail::Materialized_Model_Attachment<Middle_Data_Tag, Test_Model>>);
static_assert(!aethera::detail::Builder_Settable_Concurrent<aethera::Prop_Tag, Test_Model, int>);
static_assert(aethera::detail::Builder_Settable_Concurrent<aethera::Prop_Tag, Test_Model, decltype(&Test_Model_Base::Prop::same_name), int>);
static_assert(aethera::detail::Settable_Concurrent<aethera::Prop_Tag, Test_Model, decltype(&Test_Model_Base::Prop::same_name), int>);
@@ -168,7 +179,7 @@ TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
});
EXPECT_EQ(struct_initializer_calls, 1);
auto model = builder.build();
auto& properties = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
auto& properties = aethera::detail::model_attachment<aethera::Prop_Tag>(*model);
EXPECT_EQ(properties.internal.internal_read.base_number, 2);
EXPECT_EQ(properties.internal.internal_read.middle_number, 3);
EXPECT_EQ(properties.internal.internal_read.number, 5);
@@ -178,9 +189,9 @@ TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
EXPECT_EQ(state.middle_state, 19);
EXPECT_EQ(state.state, 23);
});
auto& numbers = aethera::detail::model_private(*model).concurrent<Number_List_Tag>();
auto& numbers = aethera::detail::model_attachment<Number_List_Tag>(*model);
EXPECT_EQ(*numbers.internal.use(), (std::vector<int>{11, 13}));
auto& other_numbers = aethera::detail::model_private(*model).concurrent<Other_Number_List_Tag>();
auto& other_numbers = aethera::detail::model_attachment<Other_Number_List_Tag>(*model);
EXPECT_EQ(*other_numbers.internal.use(), (std::vector<int>{17, 19}));
model->set<Number_List_Tag>([](Test_Model::Numbers& values) {
values.assign({17, 19});
@@ -202,8 +213,11 @@ TEST(Model, Every_Def_Layer_Shares_One_Private_And_Storage_Set) {
auto& endpoint_private = aethera::detail::model_private(*model);
EXPECT_EQ(std::addressof(aethera::detail::model_private(*middle)), std::addressof(static_cast<Test_Model_Middle::Private&>(endpoint_private)));
EXPECT_EQ(std::addressof(aethera::detail::model_private(*base)), std::addressof(static_cast<Test_Model_Base::Private&>(endpoint_private)));
auto* properties = std::addressof(endpoint_private.concurrent<aethera::Prop_Tag>());
EXPECT_EQ(properties, std::addressof(endpoint_private.concurrent<aethera::Prop_Tag>()));
auto* properties = endpoint_private.concurrent<aethera::Prop_Tag>().internal.use();
auto* middle_properties = aethera::detail::model_private(*middle).concurrent<aethera::Prop_Tag>().internal.use();
auto* base_properties = aethera::detail::model_private(*base).concurrent<aethera::Prop_Tag>().internal.use();
EXPECT_EQ(static_cast<Test_Model_Middle::Prop*>(properties), middle_properties);
EXPECT_EQ(static_cast<Test_Model_Base::Prop*>(properties), base_properties);
}
EXPECT_EQ(test_model_private_instances, instances_before);
EXPECT_EQ(test_model_base_private_instances, base_instances_before);
@@ -212,7 +226,7 @@ TEST(Model, Every_Def_Layer_Shares_One_Private_And_Storage_Set) {
TEST(Model, Registered_Struct_Set_Publishes_To_The_Same_Attachment) {
auto model = Test_Model::Builder{}.build();
model->set<aethera::Prop_Tag>(&Test_Model::Prop::number, 13);
auto& properties = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
auto& properties = aethera::detail::model_attachment<aethera::Prop_Tag>(*model);
properties.internal.advance();
EXPECT_EQ(properties.internal.use()->number, 13);
}
@@ -222,7 +236,7 @@ TEST(Model, Derived_Def_Registers_And_Uses_A_New_Concurrent) {
.set<Middle_Data_Tag>(&Test_Model_Middle::Middle_Data::middle_number, 47)
.set<Middle_Data_Tag>(&Test_Model::Middle_Data::final_number, 53);
auto model = builder.build();
auto& storage = aethera::detail::model_private(*model).concurrent<Middle_Data_Tag>();
auto& storage = aethera::detail::model_attachment<Middle_Data_Tag>(*model);
EXPECT_EQ(storage.internal.use()->middle_number, 47);
EXPECT_EQ(storage.internal.use()->final_number, 53);
model->set<Middle_Data_Tag>(&Test_Model_Middle::Middle_Data::middle_number, 59);
@@ -241,12 +255,26 @@ TEST(Model, Same_Name_Properties_In_Base_And_Derived_Layers_Are_Set_Independentl
EXPECT_EQ(properties->same_name, 31);
model->set<aethera::Prop_Tag>(&Test_Model_Base::Prop::same_name, 37);
model->set<aethera::Prop_Tag>(&Test_Model::Prop::same_name, 41);
auto& storage = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
auto& storage = aethera::detail::model_attachment<aethera::Prop_Tag>(*model);
storage.internal.advance();
properties = storage.internal.use();
EXPECT_EQ(static_cast<const Test_Model_Base::Prop&>(*properties).same_name, 37);
EXPECT_EQ(properties->same_name, 41);
}
TEST(Model, Base_Private_View_Uses_The_Final_Endpoint_Attachment) {
Test_Model::Builder builder;
builder
.set<aethera::Prop_Tag>(&Test_Model_Base::Prop::base_number, 3)
.set<aethera::Prop_Tag>(&Test_Model::Prop::number, 5);
auto model = builder.build();
auto& final_private = aethera::detail::model_private(*model);
auto& base_private = static_cast<Test_Model_Base::Private&>(final_private);
base_private.set_base_number(7);
base_private.advance_properties();
const auto* properties = final_private.concurrent<aethera::Prop_Tag>().internal.use();
EXPECT_EQ(properties->base_number, 7);
EXPECT_EQ(properties->number, 5);
}
TEST(Model, Consumer_Facade_Selects_One_Model_Private_View) {
auto model = aethera::make_model_proxy<Test_Model_Private_Facade, Test_Model>();
EXPECT_EQ(model->private_value(), 41);
@@ -1,4 +1,3 @@
#include "model/registration/Concurrent_Registration.hpp"
#include "model/attachment/list/List_Concurrent.hpp"
#include "model/attachment/structure/Struct_Concurrent.hpp"
#include "model/detail/Attachment.hpp"
@@ -18,24 +17,40 @@ struct Value {
using List = std::vector<int>;
template <typename Stored>
struct Fixed_Value_Tag {
template <typename>
using Value = Stored;
};
template <template <typename...> typename Definition, typename Stored>
using Materialized_Concurrent = typename Definition<Fixed_Value_Tag<Stored>>::template Attachment<void, Fixed_Value_Tag<Stored>>;
using Export_Struct_Attachment = Materialized_Concurrent<aethera::Export_Struct_Concurrent, Value>;
using Import_Struct_Attachment = Materialized_Concurrent<aethera::Import_Struct_Concurrent, Value>;
using Import_List_Attachment = Materialized_Concurrent<aethera::Import_List_Concurrent, List>;
using Export_List_Attachment = Materialized_Concurrent<aethera::Export_List_Concurrent, List>;
using Readable_Batch_Attachment = Materialized_Concurrent<aethera::Readable_Import_Batch_Concurrent, List>;
struct Internal_Only_Attachment {
struct Internal {
void advance();
Value* use();
const Value* use() const;
template <typename Fn> void builder_set(Fn&& fn);
} internal;
};
static_assert(aethera::Concurrent_Attachment<aethera::Export_Struct_Concurrent<Value>>);
static_assert(aethera::Concurrent_Attachment<aethera::Import_Struct_Concurrent<Value>>);
static_assert(aethera::Concurrent_Attachment<aethera::Import_List_Concurrent<List>>);
static_assert(aethera::Concurrent_Attachment<aethera::Export_List_Concurrent<List>>);
static_assert(aethera::Concurrent_Attachment<aethera::Readable_Import_Batch_Concurrent<List>>);
static_assert(aethera::Concurrent_Attachment<Export_Struct_Attachment>);
static_assert(aethera::Concurrent_Attachment<Import_Struct_Attachment>);
static_assert(aethera::Concurrent_Attachment<Import_List_Attachment>);
static_assert(aethera::Concurrent_Attachment<Export_List_Attachment>);
static_assert(aethera::Concurrent_Attachment<Readable_Batch_Attachment>);
static_assert(aethera::Concurrent_Internal<Internal_Only_Attachment>);
static_assert(!aethera::Concurrent_Attachment<Internal_Only_Attachment>);
TEST(Concurrent_Protocol, Internal_Operations_Use_The_Common_Contract) {
aethera::Import_Struct_Concurrent<Value> storage;
Import_Struct_Attachment storage;
storage.internal.builder_set([](Value& value) { value.number = 29; });
EXPECT_EQ(storage.internal.use()->number, 29);
storage.set(&Value::number, 31);
@@ -43,21 +58,27 @@ TEST(Concurrent_Protocol, Internal_Operations_Use_The_Common_Contract) {
EXPECT_EQ(storage.internal.use()->number, 31);
}
struct First_Tag {};
struct Second_Tag {};
struct First_Tag {
template <typename>
using Value = ::Value;
};
struct Second_Tag {
template <typename>
using Value = ::Value;
};
using First_Attachment =
aethera::Model_Attachment<First_Tag, aethera::Import_Struct_Concurrent<Value>>;
using Second_Attachment =
aethera::Model_Attachment<Second_Tag, aethera::Export_Struct_Concurrent<Value>>;
using First_Value_Attachment = aethera::Import_Struct_Concurrent<First_Tag>::Attachment<void, First_Tag>;
using Second_Value_Attachment = aethera::Export_Struct_Concurrent<Second_Tag>::Attachment<void, Second_Tag>;
using First_Attachment = aethera::Model_Attachment<First_Tag, First_Value_Attachment>;
using Second_Attachment = aethera::Model_Attachment<Second_Tag, Second_Value_Attachment>;
using Attachment_Set = aethera::Model_Attachment_Set<First_Attachment, Second_Attachment>;
static_assert(std::same_as<
decltype(std::declval<Attachment_Set&>().get<First_Tag>()),
aethera::Import_Struct_Concurrent<Value>&>);
First_Value_Attachment&>);
static_assert(std::same_as<
decltype(std::declval<const Attachment_Set&>().get<Second_Tag>()),
const aethera::Export_Struct_Concurrent<Value>&>);
const Second_Value_Attachment&>);
TEST(Model_Attachment, Tagged_Set_Returns_The_Selected_Attachment) {
Attachment_Set attachments;
@@ -1,5 +1,4 @@
#include "model/Model.hpp"
#include "model/registration/Relation_Registration.hpp"
#include "model/attachment/dag/Dag_Relation.hpp"
#include <gtest/gtest.h>
#include <optional>
@@ -7,7 +6,7 @@ namespace {
struct Test_Relation_Node : aethera::facade_builder
::support_relocation<pro::constraint_level::nothrow>
::build {};
struct Test_Relation_Model : aethera::Def<Test_Relation_Model, aethera::Root, aethera::Relation_Registration<aethera::Owned_Dag_Relation, Test_Relation_Node>> {
struct Test_Relation_Model : aethera::Def<Test_Relation_Model, aethera::Root, aethera::Owned_Dag_Relation<Test_Relation_Node>> {
struct Private : Prev_Private {};
};
template <typename Model>
@@ -15,6 +14,8 @@ concept Exposes_Concurrent_Model_API = requires(Model& model) { model.template s
template <typename Builder>
concept Exposes_Concurrent_Builder_API = requires(Builder& builder) { builder.template set<Test_Relation_Node>(0); };
static_assert(aethera::Relation_Attachment<aethera::Owned_Dag_Relation<Test_Relation_Node>>);
static_assert(aethera::detail::Model_Attachment_Definition<aethera::Owned_Dag_Relation<Test_Relation_Node>>);
static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<Test_Relation_Node, Test_Relation_Model>, aethera::Owned_Dag_Relation<Test_Relation_Node>>);
static_assert(aethera::detail::Relation_Model_Tag<Test_Relation_Node, Test_Relation_Model>);
static_assert(!Exposes_Concurrent_Model_API<Test_Relation_Model>);
static_assert(!Exposes_Concurrent_Builder_API<Test_Relation_Model::Builder>);
@@ -21,8 +21,17 @@ struct Derived_Dirty_Value : Base_Dirty_Value {
using List = std::vector<int>;
template <typename Stored>
struct Fixed_Value_Tag {
template <typename>
using Value = Stored;
};
template <template <typename...> typename Definition, typename Stored>
using Materialized_Concurrent = typename Definition<Fixed_Value_Tag<Stored>>::template Attachment<void, Fixed_Value_Tag<Stored>>;
struct Struct_Concurrent_Dirty_Test : testing::Test {
using Storage = aethera::Dirty_Import_Struct_Concurrent<Value>;
using Storage = Materialized_Concurrent<aethera::Dirty_Import_Struct_Concurrent, Value>;
using Member = int Value::*;
Storage storage;
aethera::Dirty_Binder<Storage> whole_dirty{storage};
@@ -108,7 +117,7 @@ TEST_F(Struct_Concurrent_Dirty_Test, Multiple_Writes_Publish_One_Whole_Revision)
}
TEST(Concurrent_Dirty, Export_Struct_Concurrent_Whole_Access_Dirties_All_Properties) {
aethera::Dirty_Export_Struct_Concurrent<Value> storage;
Materialized_Concurrent<aethera::Dirty_Export_Struct_Concurrent, Value> storage;
aethera::Dirty_Binder whole_dirty{storage};
aethera::Property_Dirty_Binder number_dirty{storage, &Value::number};
aethera::Property_Dirty_Binder other_dirty{storage, &Value::other};
@@ -122,7 +131,7 @@ TEST(Concurrent_Dirty, Export_Struct_Concurrent_Whole_Access_Dirties_All_Propert
}
TEST(Concurrent_Dirty, Binder_Created_After_Publish_Starts_From_Current_Revision) {
aethera::Dirty_Import_Struct_Concurrent<Value> storage;
Materialized_Concurrent<aethera::Dirty_Import_Struct_Concurrent, Value> storage;
storage.set(&Value::number, 33);
storage.internal.advance();
@@ -138,7 +147,7 @@ TEST(Concurrent_Dirty, Binder_Created_After_Publish_Starts_From_Current_Revision
}
TEST(Concurrent_Dirty, Inherited_Member_Write_Tracks_The_Base_Property) {
aethera::Dirty_Import_Struct_Concurrent<Derived_Dirty_Value> storage;
Materialized_Concurrent<aethera::Dirty_Import_Struct_Concurrent, Derived_Dirty_Value> storage;
aethera::Property_Dirty_Binder base_dirty{storage, &Base_Dirty_Value::base_number};
storage.set(&Base_Dirty_Value::base_number, 79);
storage.internal.advance();
@@ -147,9 +156,9 @@ TEST(Concurrent_Dirty, Inherited_Member_Write_Tracks_The_Base_Property) {
}
struct List_Concurrent_Dirty_Test : testing::Test {
using Imported = aethera::Dirty_Import_List_Concurrent<List>;
using Exported = aethera::Dirty_Export_List_Concurrent<List>;
using Batch = aethera::Dirty_Readable_Import_Batch_Concurrent<List>;
using Imported = Materialized_Concurrent<aethera::Dirty_Import_List_Concurrent, List>;
using Exported = Materialized_Concurrent<aethera::Dirty_Export_List_Concurrent, List>;
using Batch = Materialized_Concurrent<aethera::Dirty_Readable_Import_Batch_Concurrent, List>;
Imported imported;
Exported exported;
Batch batch;
@@ -175,8 +184,8 @@ TEST_F(List_Concurrent_Dirty_Test, Each_List_Write_Entry_Advances_Whole_Dirty) {
}
TEST(Concurrent_Dirty, Whole_Binder_Tracks_Each_Source_Independently) {
aethera::Dirty_Export_Struct_Concurrent<Value> first;
aethera::Dirty_Export_Struct_Concurrent<Value> second;
Materialized_Concurrent<aethera::Dirty_Export_Struct_Concurrent, Value> first;
Materialized_Concurrent<aethera::Dirty_Export_Struct_Concurrent, Value> second;
aethera::Dirty_Binder dirty{first, second};
first.internal.use()->number = 47;
@@ -8,8 +8,17 @@ namespace {
using List = std::vector<int>;
template <typename Stored>
struct Fixed_Value_Tag {
template <typename>
using Value = Stored;
};
template <template <typename...> typename Definition, typename Stored>
using Materialized_Concurrent = typename Definition<Fixed_Value_Tag<Stored>>::template Attachment<void, Fixed_Value_Tag<Stored>>;
TEST(List_Concurrent, Import_Rotates_External_Batch_To_Internal) {
aethera::Import_List_Concurrent<List> storage;
Materialized_Concurrent<aethera::Import_List_Concurrent, List> storage;
List* const internal_before = storage.internal.internal;
List* const external_before = storage.internal.external;
@@ -27,7 +36,7 @@ TEST(List_Concurrent, Import_Rotates_External_Batch_To_Internal) {
}
TEST(List_Concurrent, Export_Publishes_Produced_Batch_And_Clears_Reuse_Slot) {
aethera::Export_List_Concurrent<List> storage;
Materialized_Concurrent<aethera::Export_List_Concurrent, List> storage;
List* const internal_before = storage.internal.internal;
List* const external_before = storage.internal.external;
storage.internal.use()->assign({3, 5});
@@ -43,7 +52,7 @@ TEST(List_Concurrent, Export_Publishes_Produced_Batch_And_Clears_Reuse_Slot) {
}
TEST(List_Concurrent, Readable_Import_Batch_Concurrent_Exposes_Previous_Internal_Batch) {
aethera::Readable_Import_Batch_Concurrent<List> storage;
Materialized_Concurrent<aethera::Readable_Import_Batch_Concurrent, List> storage;
storage.set([](List& values) {
values = {7};
@@ -67,7 +76,7 @@ TEST(List_Concurrent, Readable_Import_Batch_Concurrent_Exposes_Previous_Internal
}
TEST(List_Concurrent, Readable_Import_Batch_Concurrent_Rotates_All_Three_Pointers) {
aethera::Readable_Import_Batch_Concurrent<List> storage;
Materialized_Concurrent<aethera::Readable_Import_Batch_Concurrent, List> storage;
List* const write_before = storage.internal.external_write;
List* const internal_before = storage.internal.internal_read;
List* const read_before = storage.internal.external_read;
@@ -19,19 +19,24 @@ struct Derived_Value : Base_Value {
int derived_number{};
};
template <typename Stored>
struct Fixed_Value_Tag {
template <typename>
using Value = Stored;
};
template <template <typename...> typename Definition, typename Stored>
using Materialized_Concurrent = typename Definition<Fixed_Value_Tag<Stored>>::template Attachment<void, Fixed_Value_Tag<Stored>>;
struct Struct_Hooks {
void before_advance(
const Value* internal,
const Value* external) noexcept {
void before_advance(const Value* internal, const Value* external) noexcept {
before_internal = internal;
before_external = external;
before_external_number = external->number;
before_order = ++order;
}
void after_advance(
const Value* internal,
const Value* external) noexcept {
void after_advance(const Value* internal, const Value* external) noexcept {
after_internal = internal;
after_external = external;
after_external_number = external->number;
@@ -50,7 +55,7 @@ struct Struct_Hooks {
};
TEST(Struct_Concurrent, Export_Publishes_Only_On_Advance) {
aethera::Export_Struct_Concurrent<Value> storage;
Materialized_Concurrent<aethera::Export_Struct_Concurrent, Value> storage;
storage.internal.use()->number = 7;
storage.internal.use()->text = "pending";
@@ -68,7 +73,7 @@ TEST(Struct_Concurrent, Export_Publishes_Only_On_Advance) {
}
TEST(Struct_Concurrent, Import_Consumes_Only_On_Advance) {
aethera::Import_Struct_Concurrent<Value> storage;
Materialized_Concurrent<aethera::Import_Struct_Concurrent, Value> storage;
storage.set(&Value::number, 13);
storage.set([](Value& value) {
@@ -87,7 +92,10 @@ TEST(Struct_Concurrent, Import_Consumes_Only_On_Advance) {
}
TEST(Struct_Concurrent, Advance_Hooks_Receive_Original_Pointers_In_Order) {
aethera::Export_Struct_Concurrent<Value, Struct_Hooks> storage;
struct Hooked_Export;
using Tag = Fixed_Value_Tag<Value>;
struct Hooked_Export : aethera::detail::Export_Struct_Concurrent_Definition<Hooked_Export, Struct_Hooks, Tag> {};
Hooked_Export::Attachment<void, Tag> storage;
Value* const internal = storage.internal.use();
Value* const external = &storage.internal.external_read;
internal->number = 19;
@@ -104,9 +112,11 @@ TEST(Struct_Concurrent, Advance_Hooks_Receive_Original_Pointers_In_Order) {
}
TEST(Struct_Concurrent, Inherited_Member_Pointers_Work_For_All_Struct_Interfaces) {
static_assert(aethera::Concurrent_External_Set<aethera::Import_Struct_Concurrent<Derived_Value>, decltype(&Base_Value::base_number), int>);
static_assert(aethera::Concurrent_External_Get<aethera::Import_Struct_Concurrent<Derived_Value>, decltype(&Base_Value::base_number)>);
aethera::Import_Struct_Concurrent<Derived_Value> imported;
using Imported = Materialized_Concurrent<aethera::Import_Struct_Concurrent, Derived_Value>;
using Exported = Materialized_Concurrent<aethera::Export_Struct_Concurrent, Derived_Value>;
static_assert(aethera::Concurrent_External_Set<Imported, decltype(&Base_Value::base_number), int>);
static_assert(aethera::Concurrent_External_Get<Imported, decltype(&Base_Value::base_number)>);
Imported imported;
imported.internal.builder_set(&Base_Value::base_number, 61);
EXPECT_EQ(imported.internal.use()->base_number, 61);
EXPECT_EQ(imported.get(&Base_Value::base_number), 61);
@@ -115,7 +125,7 @@ TEST(Struct_Concurrent, Inherited_Member_Pointers_Work_For_All_Struct_Interfaces
imported.internal.advance();
EXPECT_EQ(imported.internal.use()->base_number, 67);
aethera::Export_Struct_Concurrent<Derived_Value> exported;
Exported exported;
exported.internal.builder_set(&Base_Value::base_number, 71);
EXPECT_EQ(exported.get(&Base_Value::base_number), 71);
exported.internal.use()->base_number = 73;
+6 -5
View File
@@ -1,19 +1,20 @@
# Model 定义与扩展规范
- 每个 Model 定义层的 `Prop``State` 等值类型必须定义在所属类内并继承 `Prev<Tag>`,使同一 Tag 的值结构沿 Model 层级形成继承链。
- 每层统一使用 `Def<当前层, Base, Registrations...>`;中间类是普通类,不需要模板化,也不区分末尾层。
- 注册写在所属 `Def` 的模板参数上。并发结构使用 `Concurrent_Registration<Concrete, Tags...>`,关系结构使用 `Relation_Registration<Concrete, Tags...>`
- 每层统一使用 `Def<当前层, Base, Definitions...>`;中间类是普通类,不需要模板化,也不区分末尾层。
- 一段式附件定义写在所属 `Def` 的模板参数上。并发结构直接使用 `Import_Struct_Concurrent<Tags...>` 等具体类型,关系结构直接使用 `Owned_Dag_Relation<Node_Facade>`
- 同一个注册组可以连续登记多个 Tag;相同 Tag 在完整继承链只能注册一次。
- 注册只描述协议、Tag 和具体附件的映射。完整注册链按 Builder 正在构建的最终 endpoint 求值,并由公共 `Root` 一次物化唯一的 `Private + Model_Attachment_Set`
- 每个具体附件类型同时声明 Tag、能力来源和实际 `Attachment<Endpoint, Tag>`。完整附件项链按 Builder 正在构建的最终 endpoint 求值,并由公共 `Root` 一次物化唯一的 `Private + Model_Attachment_Set`
- Builder 只拥有一个最终 endpoint 对象槽;被继承的 Def 层不得创建自己的 endpoint state、对象副本、附件或 Private 指针。Private 解析器只随 Root 的唯一类型擦除 state 保存一份,定义层通过零状态 `model_private(model)` 选择自身的类型化视图。
- 基础层 Private 的 `concurrent<Tag>()` 必须由 Root 解析最终 endpoint 的实际附件并调整成当前层值视图;禁止按基础 endpoint 类型强转整套最终 state,也不得在每个 Private 层缓存附件指针。
- Model 实体必须通过 `Builder``make_model_proxy` 或接收已完成 Builder 对象的 proxy 工厂物化;普通构造不创建运行状态。
- 新协议必须放入 `model/registration``Concurrent_Registration``Relation_Registration` 同级,并自行提供 concept、注册项和 Model/Builder/Private 三组 CRTP API。具体附件统一放入 `model/attachment`禁止重新建立顶层 `concurrent``relation` 分类目录,也禁止在 `Model` 内增加协议分支。
- 新能力族必须在 `model/detail` 提供`Concurrent_Definition``Relation_Definition` 同级的 CRTP 定义基类,并自行提供 concept、注册项和 Model/Builder/Private 三组 API。具体一段式定义统一放入 `model/attachment`由类型自身继承对应定义基类并直接定义实际附件;禁止在 `Model` 内增加协议分支。
- 最终 endpoint 只继承实际注册协议的 API;未注册关系结构的 Builder 和实体不得出现关系函数,未注册并发结构的类型不得出现并发函数。
- 每个注册进 Model 的并发附件必须提供内部 `advance/use/builder_set`,外部 `set/get` 至少提供一个。Builder 的 `set<Tag>` 只进入 `internal.builder_set`,实体 `set/get` 只进入外部接口。
- 关系结构不统一成并发接口。关系实体通过 `edit<Tag>` 提交事务,Builder 通过 `relation<Tag>` 初始化,Private 在所有者安全点通过 `relation<Tag>/commit<Tag>` 使用和提交。
```cpp
struct Frame_Policy : Def<Frame_Policy, Root, Concurrent_Registration<Dirty_Import_Struct_Concurrent, Prop_Tag>, Concurrent_Registration<Export_Struct_Concurrent, State_Tag>> {
struct Frame_Policy : Def<Frame_Policy, Root, Dirty_Import_Struct_Concurrent<Prop_Tag>, Export_Struct_Concurrent<State_Tag>> {
struct Prop : Prev<Prop_Tag> {
double frames_per_second{};
};