模板优化
This commit is contained in:
@@ -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_Private、Builder 和 d;其余别名只供框架递归。 */
|
||||
/* 业务定义直接使用 Prev、Prev_Private 和 Builder;其余别名只供框架递归。 */
|
||||
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。 */
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 和实体不得出现关系函数,未注册并发结构的类型不得出现并发函数。
|
||||
|
||||
Reference in New Issue
Block a user