2D移植完成
This commit is contained in:
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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{};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user