This commit is contained in:
2026-09-03 16:57:18 +08:00
parent e31a4f342e
commit 26c1ed2e7c
23 changed files with 133 additions and 66 deletions
@@ -8,6 +8,18 @@
#include <optional>
#include <proxy/proxy.h>
namespace aethera {
namespace detail {
template <typename Tag, typename Endpoint, typename = void>
struct Dag_Node_Facade {
using Type = Tag;
};
template <typename Tag, typename Endpoint>
struct Dag_Node_Facade<Tag, Endpoint, std::void_t<typename Tag::template Relation_Node_Facade<Endpoint>>> {
using Type = typename Tag::template Relation_Node_Facade<Endpoint>;
};
} // namespace detail
struct Dag_Node_Id {
std::uint64_t value{}; /* 一张 DAG 内的稳定节点标识;0 表示未分配。 */
};
@@ -25,6 +37,7 @@ enum struct Dag_Result : std::uint8_t {
/* 拥有节点 proxy 和实例级依赖边的完整 DAG 定义;proxy 的实际持有对象由使用方决定。 */
template <typename Node_Facade>
struct Dag_Relation {
using Node = Node_Facade;
struct Builder;
struct Editor;
Dag_Relation();
@@ -82,8 +95,8 @@ struct Dag_Relation<Node_Facade>::Editor {
/* Model 可注册的拥有型 DAG 关系;内部 commit 在所有者安全点验证并提交待处理事务。 */
template <typename Node_Facade>
struct Owned_Dag_Relation : detail::Relation_Definition<Owned_Dag_Relation<Node_Facade>, Node_Facade> {
template <typename, typename Tag>
using Attachment = Owned_Dag_Relation<Tag>;
template <typename Endpoint, typename Tag>
using Attachment = Owned_Dag_Relation<typename detail::Dag_Node_Facade<Tag, Endpoint>::Type>;
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>)>;
@@ -24,6 +24,6 @@ Model 的可组合能力统一分为两层:
## 关系附件契约
关系附件必须提供权威 `Graph`、事务 `Edit`、事务完成回调,以及内部 `relation()``commit()``build(...)``Owned_Dag_Relation<Node_Facade>` 自身是一段式定义和实际附件,向最终 endpoint 选择性附加实体 `edit<Tag>`、Builder `relation<Tag>` 和 Private `relation<Tag>/commit<Tag>`
关系附件必须提供权威 `Graph`、事务 `Edit`、事务完成回调,以及内部 `relation()``commit()``build(...)``Owned_Dag_Relation<Tag>` 自身是一段式定义和实际附件,向最终 endpoint 选择性附加实体 `edit<Tag>`、Builder `relation<Tag>` 和 Private `relation<Tag>/commit<Tag>`Tag 默认同时是节点 facadeTag 可以用 `Relation_Node_Facade<Endpoint>` 为最终 endpoint 选择更窄的依赖 facade,但关系仍由原 Tag 唯一标识,禁止派生层再注册第二张同语义关系图。
`Dag_Relation::Builder` 在构建期验证完整关系;`Dag_Relation::Editor` 只记录单次事务命令;`Owned_Dag_Relation` 由关系所有者在安全点验证并提交。编辑 completion 在调用 `commit()` 的内部线程执行,不是跨线程异常传播边界。
+1 -1
View File
@@ -32,6 +32,6 @@ Root 的类型擦除 state 操作表保存一份 Private 与附件解析器,De
分别承载独占、共享和非拥有生命周期,同时把 proxy 解引用目标映射到 Model 的唯一 Private;实体公开类不实现 facade 值转发。
具体类型自身就是一段式附件定义。例如,并发结构使用 `Import_Struct_Concurrent<Tags...>`,拥有型 DAG 使用
`Owned_Dag_Relation<Node_Facade>`。业务定义不再套额外的二段式注册包装。一个并发定义可连续绑定多个
`Owned_Dag_Relation<Tag>`。Tag 默认也作为节点 facade;如果 Tag 定义 `Relation_Node_Facade<Endpoint>`,则最终 endpoint 只物化一份关系状态,并按最终类型选择节点 facade。业务定义不再套额外的二段式注册包装。一个并发定义可连续绑定多个
Tag;它继承 `Concurrent_Definition<Self, Tags...>`,并在内部直接定义最终 endpoint 所物化的 `Attachment<Endpoint, Tag>`。Builder
和实体只写 Tag,例如 `set<Prop_Tag>(...)`;具体附件及其存储值由该定义和最终 endpoint 唯一确定。
+4 -1
View File
@@ -12,5 +12,8 @@ struct Renderable : facade_builder
::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 {};
::build {
template <typename Endpoint>
using Relation_Node_Facade = typename Endpoint::Scene_Renderable_Facade;
};
} // namespace aethera::scene
-1
View File
@@ -62,7 +62,6 @@ void Scene::Private::advance_renderables() {
renderable->advance();
});
}
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));
+1
View File
@@ -9,6 +9,7 @@
namespace aethera {
/* Scene 通过 Tag + Owned_Dag_Relation 在类型系统中表达其对 Renderable DAG 的唯一所有权。 */
struct Scene : Def<Scene, Root, Owned_Dag_Relation<scene::Renderable>> {
using Scene_Renderable_Facade = scene::Renderable;
struct Private;
Scene();
~Scene() noexcept;
@@ -2,6 +2,7 @@
#include "model/attachment/dag/Dag_Relation.hpp"
#include <gtest/gtest.h>
#include <optional>
#include <type_traits>
namespace {
struct Test_Relation_Node : aethera::facade_builder
::support_relocation<pro::constraint_level::nothrow>
@@ -9,6 +10,23 @@ struct Test_Relation_Node : aethera::facade_builder
struct Test_Relation_Model : aethera::Def<Test_Relation_Model, aethera::Root, aethera::Owned_Dag_Relation<Test_Relation_Node>> {
struct Private : Prev_Private {};
};
struct Test_Adaptive_Relation_Tag : aethera::facade_builder
::support_relocation<pro::constraint_level::nothrow>
::build {
template <typename Endpoint>
using Relation_Node_Facade = typename Endpoint::Selected_Relation_Node_Facade;
};
struct Test_Narrow_Relation_Node : aethera::facade_builder
::support_relocation<pro::constraint_level::nothrow>
::build {};
struct Test_Adaptive_Relation_Base : aethera::Def<Test_Adaptive_Relation_Base, aethera::Root, aethera::Owned_Dag_Relation<Test_Adaptive_Relation_Tag>> {
using Selected_Relation_Node_Facade = Test_Adaptive_Relation_Tag;
struct Private : Prev_Private {};
};
struct Test_Adaptive_Relation_Model : aethera::Def<Test_Adaptive_Relation_Model, Test_Adaptive_Relation_Base> {
using Selected_Relation_Node_Facade = Test_Narrow_Relation_Node;
struct Private : Prev_Private {};
};
template <typename Model>
concept Exposes_Concurrent_Model_API = requires(Model& model) { model.template set<Test_Relation_Node>(0); };
template <typename Builder>
@@ -19,6 +37,7 @@ static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<Test_R
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>);
static_assert(std::same_as<aethera::detail::Materialized_Model_Attachment<Test_Adaptive_Relation_Tag, Test_Adaptive_Relation_Model>, aethera::Owned_Dag_Relation<Test_Narrow_Relation_Node>>);
TEST(Dag_Relation, Registration_Composes_Only_Relation_APIs) {
bool initialized{};
Test_Relation_Model::Builder builder;
@@ -42,4 +61,16 @@ TEST(Dag_Relation, Registration_Composes_Only_Relation_APIs) {
aethera::detail::model_private(*model).relation<Test_Relation_Node>().for_each_topological([&](auto, auto&) { ++nodes; });
EXPECT_EQ(nodes, 0);
}
TEST(Dag_Relation, Inherited_Tag_Selects_Final_Endpoint_Node_Facade_Without_Second_Relation) {
bool selected{};
Test_Adaptive_Relation_Model::Builder builder;
builder
.relation<Test_Adaptive_Relation_Tag>([&](auto& relation) {
static_assert(std::same_as<typename std::remove_reference_t<decltype(relation)>::Node, Test_Narrow_Relation_Node>);
selected = true;
});
auto model = builder.build();
EXPECT_TRUE(model);
EXPECT_TRUE(selected);
}
} // namespace
+1 -1
View File
@@ -2,7 +2,7 @@
- 每个 Model 定义层的 `Prop``State` 等值类型必须定义在所属类内并继承 `Prev<Tag>`,使同一 Tag 的值结构沿 Model 层级形成继承链。
- 每层统一使用 `Def<当前层, Base, Definitions...>`;中间类是普通类,不需要模板化,也不区分末尾层。
- 一段式附件定义写在所属 `Def` 的模板参数上。并发结构直接使用 `Import_Struct_Concurrent<Tags...>` 等具体类型,关系结构直接使用 `Owned_Dag_Relation<Node_Facade>`
- 一段式附件定义写在所属 `Def` 的模板参数上。并发结构直接使用 `Import_Struct_Concurrent<Tags...>` 等具体类型,关系结构直接使用 `Owned_Dag_Relation<Tag>`。Tag 默认同时是节点 facade;需要让派生 endpoint 使用模块专用 facade 时,由 Tag 的 `Relation_Node_Facade<Endpoint>` 选择实际节点 facade,派生层不得重复注册同一语义的关系状态
- 同一个注册组可以连续登记多个 Tag;相同 Tag 在完整继承链只能注册一次。
- 每个具体附件类型同时声明 Tag、能力来源和实际 `Attachment<Endpoint, Tag>`。完整附件项链按 Builder 正在构建的最终 endpoint 求值,并由公共 `Root` 一次物化唯一的 `Private + Model_Attachment_Set`
- Builder 只拥有一个最终 endpoint 对象槽;被继承的 Def 层不得创建自己的 endpoint state、对象副本、附件或 Private 指针。Private 解析器只随 Root 的唯一类型擦除 state 保存一份,定义层通过零状态 `model_private(model)` 选择自身的类型化视图。