模板优化

This commit is contained in:
2026-09-03 09:36:47 +08:00
parent b2502cedea
commit ee7dd69198
12 changed files with 124 additions and 122 deletions
+43 -50
View File
@@ -58,28 +58,22 @@ struct Model_Private_Access_Root {
template <typename Endpoint, typename Base_Private>
struct Model_Private_Base;
template <typename Endpoint>
struct Model_Private_Handle {
explicit Model_Private_Handle(Model_Instance* value) noexcept;
decltype(auto) operator*() const noexcept;
auto operator->() const noexcept;
template <typename Active_Endpoint>
void bind() noexcept;
private:
Model_Instance* instance{}; /* 非拥有借用;指向外层 Model 的 Root 子对象。 */
void* (* private_view)(void*){}; /* 将唯一实际 Private 调整为当前 Def 层的 Private 视图。 */
};
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 Model_Pointer>
struct Model_Private_Pointer {
using element_type = std::remove_reference_t<decltype(*std::declval<Model_Pointer&>()->d)>;
using element_type = std::remove_reference_t<decltype(model_private(*std::declval<Model_Pointer&>()))>;
decltype(auto) operator*() const noexcept;
Model_Pointer model; /* 裸指针为借用;智能指针按自身类型拥有 Model。 */
};
struct Model_State_Deleter {
void operator()(void* value) const noexcept;
void (*destroy)(void*){}; /* 与被擦除的实际 endpoint state 匹配。 */
/* Root 的唯一类型擦除 state 同时携带销毁和 Private 基类调整入口。 */
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。 */
};
struct Model_Instance {
@@ -90,30 +84,30 @@ struct Model_Instance {
Model_Instance(Model_Instance&&) = delete;
Model_Instance& operator=(Model_Instance&&) = delete;
protected:
[[nodiscard]] bool initialized() const noexcept;
[[nodiscard]] bool initialized() const noexcept;
private:
template <typename>
friend struct Model_Private_Handle;
template <typename, typename>
friend struct Model_Private_Base;
template <typename>
friend struct Model_Builder_Instance;
template <typename Endpoint, typename... Args>
void initialize_endpoint(Args&&... args);
template <typename Endpoint>
Model_State<Endpoint>& state() noexcept;
std::unique_ptr<void, Model_State_Deleter> state_owner; /* 唯一拥有实际 Private 与完整附件集合。 */
template <typename, typename>
friend struct Model_Private_Base;
template <typename>
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 Endpoint, typename... Args>
void initialize_endpoint(Args&&... args);
template <typename Endpoint>
Model_State<Endpoint>& state() noexcept;
std::unique_ptr<void, Model_State_Operations> state_owner; /* 唯一拥有实际 Private、附件及类型操作。 */
};
template <typename Self>
struct Model_Builder_Instance {
protected:
template <typename... Args>
void initialize_model(Args&&... args);
Self& model();
std::unique_ptr<Self> release_model();
template <typename... Args>
void initialize_model(Args&&... args);
Self& model();
std::unique_ptr<Self> release_model();
private:
std::unique_ptr<Self> model_owner; /* 构建期唯一拥有尚未发布的最终 Model。 */
std::unique_ptr<Self> model_owner; /* 构建期唯一拥有尚未发布的最终 Model。 */
};
} // namespace detail
struct Prop_Tag {
@@ -132,32 +126,31 @@ struct Root : detail::Model_Instance {
struct Private {};
protected:
Root() = default;
~Root() = default;
Root() = default;
~Root() = default;
};
/* 业务定义直接使用 Prev、Prev_PrivateBuilder 和 d;其余别名只供框架递归。 */
/* 业务定义直接使用 Prev、Prev_PrivateBuilder;其余别名只供框架递归。 */
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
struct Def : detail::Model_Definition<Self, Base, Registrations...>::Public_Base {
private:
using Definition = detail::Model_Definition<Self, Base, Registrations...>;
using Definition = detail::Model_Definition<Self, Base, Registrations...>;
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;
template <typename Tag>
using Prev = detail::Model_Value<Tag, Base, Model_Registrations>;
using Prev_Private = typename Definition::Private_Base;
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;
template <typename Tag>
using Prev = detail::Model_Value<Tag, Base, Model_Registrations>;
using Prev_Private = typename Definition::Private_Base;
struct Builder : Definition::Builder_Base {
Builder() = default;
template <typename First, typename... Rest>
explicit Builder(First&& first, Rest&&... rest);
std::unique_ptr<Self> build();
};
struct Builder : Definition::Builder_Base {
Builder() = default;
template <typename First, typename... Rest>
explicit Builder(First&& first, Rest&&... rest);
std::unique_ptr<Self> build();
};
Def();
[[nodiscard]] bool model_initialized() const noexcept;
detail::Model_Private_Handle<Self> d; /* 非拥有类型化视图;状态始终由公共 Root 唯一拥有。 */
Def() = default;
[[nodiscard]] bool model_initialized() const noexcept;
};
/* proxy 的指针种类决定 Model 生命周期;facade 始终只看最终 Private。 */
+26 -29
View File
@@ -127,13 +127,26 @@ struct Model_State : Endpoint_Attachments<Endpoint, typename Endpoint::Model_Reg
typename Endpoint::Private private_data; /* 唯一实际 Private,包含完整的 Private 继承链。 */
};
/* 每个 Def::d 都绑定到同一个最终 Private,只改变可见的基类视图。 */
/* Root 只保存一个解析函数;类型 token 选择最终 Private 中对应的定义层基类。 */
template <typename Active_Endpoint, typename... Endpoints>
void bind_model_handles(Active_Endpoint& model, Model_Type_List<Endpoints...>) noexcept {
((static_cast<Endpoints&>(model).d.template bind<Active_Endpoint>()), ...);
void* resolve_model_private(void* state, const void* token, Model_Type_List<Endpoints...>) noexcept {
auto& private_data = static_cast<Model_State<Active_Endpoint>*>(state)->private_data;
void* result{};
((token == &Model_Private_Type_Token<Endpoints> ? result = static_cast<typename Endpoints::Private*>(std::addressof(private_data)) : nullptr), ...);
return result;
}
inline Model_Instance::Model_Instance() : state_owner(nullptr, Model_State_Deleter{}) {}
template <typename Endpoint>
void destroy_model_state(void* state) noexcept {
delete static_cast<Model_State<Endpoint>*>(state);
}
template <typename Endpoint>
void* resolve_model_state_private(void* state, const void* token) noexcept {
return resolve_model_private<Endpoint>(state, token, typename Endpoint::Model_Layers{});
}
inline Model_Instance::Model_Instance() : state_owner(nullptr, Model_State_Operations{}) {}
inline Model_Instance::~Model_Instance() = default;
@@ -141,7 +154,7 @@ inline bool Model_Instance::initialized() const noexcept {
return static_cast<bool>(state_owner);
}
inline void Model_State_Deleter::operator()(void* value) const noexcept {
inline void Model_State_Operations::operator()(void* value) const noexcept {
if (destroy) destroy(value);
}
@@ -165,10 +178,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_Deleter>{state.release(), Model_State_Deleter{[](void* value) {
delete static_cast<Model_State<Endpoint>*>(value);
}}};
bind_model_handles(static_cast<Endpoint&>(*this), typename Endpoint::Model_Layers{});
state_owner = std::unique_ptr<void, Model_State_Operations>{state.release(), Model_State_Operations{destroy_model_state<Endpoint>, resolve_model_state_private<Endpoint>}};
}
template <typename Endpoint>
@@ -177,30 +187,20 @@ Model_State<Endpoint>& Model_Instance::state() noexcept {
}
template <typename Endpoint>
Model_Private_Handle<Endpoint>::Model_Private_Handle(Model_Instance* value) noexcept : instance(value) {}
template <typename Endpoint>
template <typename Active_Endpoint>
void Model_Private_Handle<Endpoint>::bind() noexcept {
private_view = [](void* state) -> void* {
auto& private_data = static_cast<Model_State<Active_Endpoint>*>(state)->private_data;
return static_cast<typename Endpoint::Private*>(std::addressof(private_data));
};
typename Endpoint::Private& model_private(Endpoint& model) noexcept {
auto& instance = static_cast<Model_Instance&>(model);
return *static_cast<typename Endpoint::Private*>(instance.state_owner.get_deleter().resolve_private(instance.state_owner.get(), &Model_Private_Type_Token<Endpoint>));
}
template <typename Endpoint>
decltype(auto) Model_Private_Handle<Endpoint>::operator*() const noexcept {
return *static_cast<typename Endpoint::Private*>(private_view(instance->state_owner.get()));
}
template <typename Endpoint>
auto Model_Private_Handle<Endpoint>::operator->() const noexcept {
return std::addressof(operator*());
const typename Endpoint::Private& model_private(const Endpoint& model) noexcept {
const auto& instance = static_cast<const Model_Instance&>(model);
return *static_cast<const typename Endpoint::Private*>(instance.state_owner.get_deleter().resolve_private(instance.state_owner.get(), &Model_Private_Type_Token<Endpoint>));
}
template <typename Model_Pointer>
decltype(auto) Model_Private_Pointer<Model_Pointer>::operator*() const noexcept {
return *model->d;
return model_private(*model);
}
template <typename Self>
@@ -226,9 +226,6 @@ 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>
Def<Self, Base, Registrations...>::Def() : d{static_cast<detail::Model_Instance*>(this)} {}
template <typename Self, typename Base, detail::Model_Registration_Group... Registrations>
template <typename First, typename... Rest>
Def<Self, Base, Registrations...>::Builder::Builder(First&& first, Rest&&... rest) {
+3 -2
View File
@@ -24,8 +24,9 @@ Builder 在构建具体 endpoint 时,才由公共 `Root` 一次物化该 endpo
的待发布对象是私有唯一所有权,只能通过 `build()` 移出。被继承的定义层只贡献注册、值类型和 Private 基类,不创建自己的状态。相同
Tag 重复注册属于编译期错误。
每层 `d` 都是对 Root 唯一状态的非拥有类型化视图。最终层 `d` 返回最终 Private;模型转换为某个基类后,该基类的 `d` 返回同一最终
Private 对象中的对应基类子对象。Model 实体必须通过 Builder 或 Model proxy 工厂物化;普通构造只建立定义对象,不产生运行状态。
Root 的类型擦除 state 操作表只保存一份 Private 解析器,Def 层不再保存 `d` 指针。框架内部通过零状态
`model_private(model)` 按模型的静态层选择类型化视图;模型转换为某个基类后,仍返回同一最终 Private 对象中的对应基类子对象。Model
实体必须通过 Builder 或 Model proxy 工厂物化;普通构造只建立定义对象,不产生运行状态。
跨模块 facade 直接声明消费方需要的 Private 能力。`make_model_proxy``make_model_proxy_shared``model_proxy_view`
分别承载独占、共享和非拥有生命周期,同时把 proxy 解引用目标映射到 Model 的唯一 Private;实体公开类不实现 facade 值转发。
@@ -17,19 +17,19 @@ void initialize_concurrent_value(Value& target, 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);
self.d->template concurrent<Tag>().set(std::forward<Args>(args)...);
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);
self.d->template concurrent<Tag>().get(std::forward<Args>(args)...);
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 = this->model().d->template concurrent<Tag>();
auto& concurrent = model_private(this->model()).template concurrent<Tag>();
concurrent.internal.builder_set([&](auto& value) {
initialize_concurrent_value(value, std::forward<Args>(args)...);
});
@@ -3,13 +3,13 @@ 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);
self.d->template relation_attachment<Tag>().edit(std::forward<Args>(args)...);
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);
this->model().d->template relation_attachment<Tag>().internal.build(std::forward<Fn>(fn));
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>
@@ -13,15 +13,15 @@ auto positive_duration(std::chrono::steady_clock::duration duration) noexcept ->
Throttled_Latest_only::Throttled_Latest_only() = default;
Throttled_Latest_only::~Throttled_Latest_only() noexcept {
if (!model_initialized()) return;
if (!d->destructible()) {
if (!detail::model_private(*this).destructible()) {
std::terminate();
}
}
auto Throttled_Latest_only::start() -> std::expected<void, Start_Result> {
return d->start();
return detail::model_private(*this).start();
}
auto Throttled_Latest_only::stop(Stop_Completion completion) -> std::expected<void, Stop_Result> {
return d->stop(std::move(completion));
return detail::model_private(*this).stop(std::move(completion));
}
Throttled_Latest_only::Private::Private(proxy<frame_policy::Timer_Service> timer_service, proxy<frame_policy::Scene> scene, proxy<frame_policy::Sink> sink) : timer_service(std::move(timer_service)), scene(std::move(scene)), sink(std::move(sink)) {}
auto Throttled_Latest_only::Private::start() -> std::expected<void, Start_Result> {
+16 -12
View File
@@ -39,11 +39,12 @@ TEST(Scene, Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) {
auto graph = builder.build();
ASSERT_TRUE(graph.has_value());
auto scene = aethera::Scene::Builder{}.build();
EXPECT_TRUE(scene->d->create_frame());
auto& scene_private = aethera::detail::model_private(*scene);
EXPECT_TRUE(scene_private.create_frame());
scene->edit<aethera::scene::Renderable>(std::move(*graph));
EXPECT_TRUE(scene->d->advance().has_value());
EXPECT_TRUE(scene_private.advance().has_value());
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
EXPECT_FALSE(scene->d->taskflow()->empty());
EXPECT_FALSE(scene_private.taskflow()->empty());
advance_order->clear();
std::optional<aethera::Dag_Result> edit_error;
scene->edit<aethera::scene::Renderable>([](aethera::Dag_Relation<aethera::scene::Renderable>::Editor& editor) {
@@ -51,7 +52,7 @@ TEST(Scene, Advances_Renderables_And_Composes_Task_Graph_In_Dag_Order) {
}, [&](std::expected<void, aethera::Dag_Result> result) {
if (!result) edit_error = result.error();
});
EXPECT_TRUE(scene->d->advance().has_value());
EXPECT_TRUE(scene_private.advance().has_value());
ASSERT_TRUE(edit_error.has_value());
EXPECT_EQ(*edit_error, aethera::Dag_Result::node_not_found);
EXPECT_EQ(*advance_order, (std::vector<int>{1, 2, 3}));
@@ -76,7 +77,8 @@ TEST(Scene, Owned_Dag_Rolls_Back_The_Whole_Invalid_Edit) {
ASSERT_TRUE(graph.has_value());
auto scene = aethera::Scene::Builder{}.build();
scene->edit<aethera::scene::Renderable>(std::move(*graph));
ASSERT_TRUE(scene->d->advance().has_value());
auto& scene_private = aethera::detail::model_private(*scene);
ASSERT_TRUE(scene_private.advance().has_value());
advance_order->clear();
std::optional<aethera::Dag_Result> edit_error;
scene->edit<aethera::scene::Renderable>([advance_order](aethera::Dag_Relation<aethera::scene::Renderable>::Editor& editor) {
@@ -85,26 +87,27 @@ TEST(Scene, Owned_Dag_Rolls_Back_The_Whole_Invalid_Edit) {
}, [&](std::expected<void, aethera::Dag_Result> result) {
if (!result) edit_error = result.error();
});
ASSERT_TRUE(scene->d->advance().has_value());
ASSERT_TRUE(scene_private.advance().has_value());
ASSERT_TRUE(edit_error.has_value());
EXPECT_EQ(*edit_error, aethera::Dag_Result::node_not_found);
EXPECT_EQ(*advance_order, (std::vector<int>{1}));
}
TEST(Scene, Render_Returns_Known_Submission_Result) {
auto scene = aethera::Scene::Builder{}.build();
auto frame = scene->d->create_frame();
auto& scene_private = aethera::detail::model_private(*scene);
auto frame = scene_private.create_frame();
bool completed{};
const auto unavailable_runtime = scene->d->render(frame, [&](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {
const auto unavailable_runtime = scene_private.render(frame, [&](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {
completed = true;
});
ASSERT_FALSE(unavailable_runtime);
EXPECT_EQ(std::get<aethera::Run_Taskflow_Result>(unavailable_runtime.error()), aethera::Run_Taskflow_Result::runtime_not_initialized);
EXPECT_FALSE(completed);
aethera::proxy<aethera::scene::Frame> empty_frame;
const auto missing_frame = scene->d->render(empty_frame, [&](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {});
const auto missing_frame = scene_private.render(empty_frame, [&](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {});
ASSERT_FALSE(missing_frame);
EXPECT_EQ(std::get<aethera::scene::Render_State_Result>(missing_frame.error()), aethera::scene::Render_State_Result::frame_missing);
const auto missing_completion = scene->d->render(frame, {});
const auto missing_completion = scene_private.render(frame, {});
ASSERT_FALSE(missing_completion);
EXPECT_EQ(std::get<aethera::scene::Render_State_Result>(missing_completion.error()), aethera::scene::Render_State_Result::completion_missing);
}
@@ -117,8 +120,9 @@ TEST(Scene, Rejects_The_Same_Renderable_Task_Graph_Twice) {
ASSERT_TRUE(graph.has_value());
auto scene = aethera::Scene::Builder{}.build();
scene->edit<aethera::scene::Renderable>(std::move(*graph));
auto frame = scene->d->create_frame();
const auto rendered = scene->d->render(frame, [](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {});
auto& scene_private = aethera::detail::model_private(*scene);
auto frame = scene_private.create_frame();
const auto rendered = scene_private.render(frame, [](aethera::proxy<aethera::scene::Frame>&, std::exception_ptr) {});
ASSERT_FALSE(rendered);
EXPECT_EQ(std::get<aethera::Compose_Task_Graph_Result>(rendered.error()), aethera::Compose_Task_Graph_Result::already_composed);
}
+20 -14
View File
@@ -92,6 +92,8 @@ template <typename Model>
concept Exposes_Relation_Model_API = requires(Model& model) { model.template edit<aethera::Prop_Tag>(0); };
template <typename Builder>
concept Exposes_Relation_Builder_API = requires(Builder& builder) { builder.template relation<aethera::Prop_Tag>([](auto&) {}); };
template <typename Model>
concept Stores_Per_Layer_Private_Handle = requires(Model& model) { model.d; };
static_assert(std::derived_from<Test_Model_Middle::Prop, Test_Model_Base::Prop>);
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>);
@@ -110,6 +112,9 @@ static_assert(aethera::detail::Builder_Settable_Concurrent<Middle_Data_Tag, Test
static_assert(aethera::detail::Settable_Concurrent<Middle_Data_Tag, Test_Model, decltype(&Test_Model_Middle::Middle_Data::middle_number), int>);
static_assert(!Exposes_Relation_Model_API<Test_Model>);
static_assert(!Exposes_Relation_Builder_API<Test_Model::Builder>);
static_assert(!Stores_Per_Layer_Private_Handle<Test_Model_Base>);
static_assert(!Stores_Per_Layer_Private_Handle<Test_Model_Middle>);
static_assert(!Stores_Per_Layer_Private_Handle<Test_Model>);
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Model*>, Test_Model_Private_Facade>);
static_assert(pro::proxiable<aethera::detail::Model_Private_Pointer<Test_Model*>, Other_Test_Model_Private_Facade>);
TEST(Model, Every_Def_Layer_Can_Materialize_Its_Own_Endpoint) {
@@ -122,7 +127,7 @@ TEST(Model, Every_Def_Layer_Can_Materialize_Its_Own_Endpoint) {
auto model = builder.build();
EXPECT_EQ(test_model_base_private_instances, base_instances_before + 1);
EXPECT_EQ(test_model_middle_private_instances, middle_instances_before);
EXPECT_EQ(model->d->concurrent<aethera::Prop_Tag>().internal.use()->base_number, 3);
EXPECT_EQ(aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>().internal.use()->base_number, 3);
}
EXPECT_EQ(test_model_base_private_instances, base_instances_before);
{
@@ -133,8 +138,8 @@ TEST(Model, Every_Def_Layer_Can_Materialize_Its_Own_Endpoint) {
auto model = builder.build();
EXPECT_EQ(test_model_base_private_instances, base_instances_before + 1);
EXPECT_EQ(test_model_middle_private_instances, middle_instances_before + 1);
EXPECT_EQ(model->d->concurrent<aethera::Prop_Tag>().internal.use()->base_number, 5);
EXPECT_EQ(model->d->concurrent<aethera::Prop_Tag>().internal.use()->middle_number, 7);
EXPECT_EQ(aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>().internal.use()->base_number, 5);
EXPECT_EQ(aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>().internal.use()->middle_number, 7);
}
EXPECT_EQ(test_model_base_private_instances, base_instances_before);
EXPECT_EQ(test_model_middle_private_instances, middle_instances_before);
@@ -163,7 +168,7 @@ TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
});
EXPECT_EQ(struct_initializer_calls, 1);
auto model = builder.build();
auto& properties = model->d->concurrent<aethera::Prop_Tag>();
auto& properties = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
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);
@@ -173,9 +178,9 @@ TEST(Model, Registered_Value_Types_Build_Independent_Storages) {
EXPECT_EQ(state.middle_state, 19);
EXPECT_EQ(state.state, 23);
});
auto& numbers = model->d->concurrent<Number_List_Tag>();
auto& numbers = aethera::detail::model_private(*model).concurrent<Number_List_Tag>();
EXPECT_EQ(*numbers.internal.use(), (std::vector<int>{11, 13}));
auto& other_numbers = model->d->concurrent<Other_Number_List_Tag>();
auto& other_numbers = aethera::detail::model_private(*model).concurrent<Other_Number_List_Tag>();
EXPECT_EQ(*other_numbers.internal.use(), (std::vector<int>{17, 19}));
model->set<Number_List_Tag>([](Test_Model::Numbers& values) {
values.assign({17, 19});
@@ -194,10 +199,11 @@ TEST(Model, Every_Def_Layer_Shares_One_Private_And_Storage_Set) {
EXPECT_EQ(test_model_private_instances, instances_before + 1);
EXPECT_EQ(test_model_base_private_instances, base_instances_before + 1);
EXPECT_EQ(test_model_middle_private_instances, middle_instances_before + 1);
EXPECT_EQ(std::addressof(*middle->d), std::addressof(static_cast<Test_Model_Middle::Private&>(*model->d)));
EXPECT_EQ(std::addressof(*base->d), std::addressof(static_cast<Test_Model_Base::Private&>(*model->d)));
auto* properties = std::addressof(model->d->concurrent<aethera::Prop_Tag>());
EXPECT_EQ(properties, std::addressof(model->d->concurrent<aethera::Prop_Tag>()));
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>()));
}
EXPECT_EQ(test_model_private_instances, instances_before);
EXPECT_EQ(test_model_base_private_instances, base_instances_before);
@@ -206,7 +212,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 = model->d->concurrent<aethera::Prop_Tag>();
auto& properties = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
properties.internal.advance();
EXPECT_EQ(properties.internal.use()->number, 13);
}
@@ -216,7 +222,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 = model->d->concurrent<Middle_Data_Tag>();
auto& storage = aethera::detail::model_private(*model).concurrent<Middle_Data_Tag>();
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);
@@ -230,12 +236,12 @@ TEST(Model, Same_Name_Properties_In_Base_And_Derived_Layers_Are_Set_Independentl
.set<aethera::Prop_Tag>(&Test_Model_Base::Prop::same_name, 29)
.set<aethera::Prop_Tag>(&Test_Model::Prop::same_name, 31);
auto model = builder.build();
const auto* properties = model->d->concurrent<aethera::Prop_Tag>().internal.use();
const auto* properties = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>().internal.use();
EXPECT_EQ(static_cast<const Test_Model_Base::Prop&>(*properties).same_name, 29);
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 = model->d->concurrent<aethera::Prop_Tag>();
auto& storage = aethera::detail::model_private(*model).concurrent<aethera::Prop_Tag>();
storage.internal.advance();
properties = storage.internal.use();
EXPECT_EQ(static_cast<const Test_Model_Base::Prop&>(*properties).same_name, 37);
@@ -33,12 +33,12 @@ TEST(Dag_Relation, Registration_Composes_Only_Relation_APIs) {
std::optional<std::expected<void, aethera::Dag_Result>> completion;
model->edit<Test_Relation_Node>([](auto& editor) { editor.remove({1}); }, [&](auto result) { completion.emplace(std::move(result)); });
EXPECT_FALSE(completion.has_value());
model->d->commit<Test_Relation_Node>();
aethera::detail::model_private(*model).commit<Test_Relation_Node>();
ASSERT_TRUE(completion.has_value());
ASSERT_FALSE(completion->has_value());
EXPECT_EQ(completion->error(), aethera::Dag_Result::node_not_found);
std::size_t nodes{};
model->d->relation<Test_Relation_Node>().for_each_topological([&](auto, auto&) { ++nodes; });
aethera::detail::model_private(*model).relation<Test_Relation_Node>().for_each_topological([&](auto, auto&) { ++nodes; });
EXPECT_EQ(nodes, 0);
}
} // namespace
+1 -1
View File
@@ -5,7 +5,7 @@
- 注册写在所属 `Def` 的模板参数上。并发结构使用 `Concurrent_Registration<Concrete, Tags...>`,关系结构使用 `Relation_Registration<Concrete, Tags...>`
- 同一个注册组可以连续登记多个 Tag;相同 Tag 在完整继承链只能注册一次。
- 注册只描述协议、Tag 和具体附件的映射。完整注册链按 Builder 正在构建的最终 endpoint 求值,并由公共 `Root` 一次物化唯一的 `Private + Model_Attachment_Set`
- Builder 只拥有一个最终 endpoint 对象槽;被继承的 Def 层不得创建自己的 endpoint state、对象副本附件。每层 `d` 只是同一 Root 状态的非拥有 Private 视图。
- Builder 只拥有一个最终 endpoint 对象槽;被继承的 Def 层不得创建自己的 endpoint state、对象副本附件或 Private 指针。Private 解析器只随 Root 的唯一类型擦除 state 保存一份,定义层通过零状态 `model_private(model)` 选择自身的类型化视图。
- Model 实体必须通过 `Builder``make_model_proxy` 或接收已完成 Builder 对象的 proxy 工厂物化;普通构造不创建运行状态。
- 新协议必须放入 `model/registration`,与 `Concurrent_Registration``Relation_Registration` 同级,并自行提供 concept、注册项和 Model/Builder/Private 三组 CRTP API。具体附件统一放入 `model/attachment`,禁止重新建立顶层 `concurrent``relation` 分类目录,也禁止在 `Model` 内增加协议分支。
- 最终 endpoint 只继承实际注册协议的 API;未注册关系结构的 Builder 和实体不得出现关系函数,未注册并发结构的类型不得出现并发函数。