diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..9d6b0a2
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,42 @@
+## 设计约束
+
+我使用 里的这个环境 D:\ae\proj\Aethera\CMakePresets.json "toolchain/vs2022.json"
+====================[ 构建 | Aethera_Kernel_check | vs2022_debug ]================
+"C:\Program Files\JetBrains\CLion 2026.1\bin\cmake\win\x64\bin\cmake.exe" --build D:
+\ae\proj\Aethera\cmake-build-vs2022_debug --target Aethera_Kernel_check -j 30 默认每次运行程序都通过CDB运行 D:
+\ae\ewdk\EWDK_22621_230929-1800\Program Files\Windows Kits\10\Debuggers\x64\cdb.exe
+
+编译器环境脚本 C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\vsdevcmd\ext\VsDevCmd.bat
+-host_arch=x64 -arch=x64
+
+D:\ae\tools 可能会有有用的工具
+
+任何 fallback 都必须先规划,禁止直接实现
+
+写函数 和模块使用下面的约定
+[错误处理规范](./Error_handling_specification.md)
+[命名约定](./Project_naming_conventions.md)
+
+0. 跟你审计报告 就要大刀阔斧的改 一次性先把问题一次改完 在批量检查 要大步前进
+1. 每个状态只能有一个权威来源,禁止在不同对象中重复保存并手工同步。
+2. 能通过计算、查询或快照得到的数据,不要保存为成员变量。
+3. 禁止增加只做值转发的成员、getter、setter 和兼容中间层。
+4. 公共接口只表达业务语义,不泄漏缓冲区角色、线程状态、Private 类型和内部指针。
+5. 接口必须正交,避免语义重叠、调用顺序依赖和多套相同实现。
+6. 类只保存自身职责需要的状态,不替其他对象保存配置或运行状态。
+7. 不要用大量默认空实现的虚函数,能力应按需拆分和组合。
+8. 不要提前加入空配置、无消费者统计和未完成接口。
+9. 替换实现时直接删除旧实现,不保留兼容转发。
+10. 修改后检查新增成员的写入者、读取者、生命周期,以及是否形成重复状态源。
+
+11. 注意使用 clion提供的工具 Name Url Bearer Token Env Var Status Auth
+ clion-index http://127.0.0.1:29177/index-mcp/streamable-http - enabled Unsupported
+
+"C:\Program Files\JetBrains\CLion 2026.1\bin\cmake\win\x64\bin\cmake.exe" -DCMAKE_BUILD_TYPE=Debug
+"-DCMAKE_C_COMPILER=C:/Program Files/Microsoft Visual
+Studio/18/Enterprise/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.bat" "-DCMAKE_CXX_COMPILER=C:/Program Files/Microsoft
+Visual Studio/18/Enterprise/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64/cl.bat" --preset vs2019_Debug -S D:\ae\proj\Radio
+-B D:\ae\proj\Radio\cmake-build-vs2019_debug
+"C:\Program Files\JetBrains\CLion 2026.1\bin\cmake\win\x64\bin\cmake.exe" --build D:
+\ae\proj\Radio\cmake-build-vs2019_debug --target Renderive_Core -j 30
+
diff --git a/Error_handling_specification.md b/Error_handling_specification.md
new file mode 100644
index 0000000..950edae
--- /dev/null
+++ b/Error_handling_specification.md
@@ -0,0 +1,130 @@
+# 统一错误处理原则
+
+## 1. 已知结果处理规则
+
+属于 API 契约允许出现、调用方能够明确处理的情况,使用返回码表达。
+
+返回码不等于错误码,`timeout`、`cancelled`、`not_ready`、`no_change` 等都可以是正常结果。
+
+判断依据只有一个:
+
+**该结果是否可预料,并且调用方是否具有明确的处理方式。**
+
+不按“内部/外部”区分。
+
+### 返回码定义规则
+
+**每个存在返回码的函数必须拥有自己独立的返回码枚举,不得在一个类或模块中定义大而通用的公共返回码枚举供多个函数混用。**
+
+例如:
+
+```cpp
+enum class Request_Frame_Result {
+ not_ready,
+ cancelled
+};
+
+std::expected request_frame();
+```
+
+```cpp
+enum class Resize_Result {
+ no_change,
+ unsupported
+};
+
+Resize_Result resize(Size size);
+```
+
+禁止:
+
+```cpp
+enum class Scene_Result {
+ not_ready,
+ cancelled,
+ no_change,
+ unsupported,
+ no_pending_frame,
+ ...
+};
+```
+
+然后由多个函数共同返回 `Scene_Result`。
+
+规则如下:
+
+* 有正常返回值,同时存在返回码:
+
+ ```cpp
+ std::expected
+ ```
+* 没有额外正常返回值,返回码本身即可完整表达结果:
+
+ ```cpp
+ Xxx_Result
+ ```
+* 没有已知返回码:
+
+ ```cpp
+ T
+ void
+ ```
+* 返回码类型必须对应具体函数的契约,函数之间不得为了减少枚举数量而合并返回码。
+
+这样可以直接从函数签名确定该函数所有需要调用方处理的已知结果。
+
+## 2. 未知失败处理规则
+
+不属于正常结果空间,或者当前调用路径没有可靠恢复方式的情况,统一视为 **Unknown Failure**。
+
+Unknown Failure 不得转换成 `unknown_error`、`internal_error` 等普通返回码。
+
+中间层没有恢复能力时不得层层处理,不增加无意义的 `catch`、包装或 catch/rethrow。
+
+Unknown Failure 应直接退出当前执行路径,由上层真正具有故障隔离能力的 request、task、worker、进程或服务边界处理。
+
+异步跨线程时可以使用 `std::exception_ptr`、`promise::set_exception()`、`future::get()` 搬运失败;这只是 transport,不属于错误处理。
+
+## 3. Unknown Failure Policy
+
+Unknown Failure 支持两种处置策略:
+
+```text
+fast_fail
+ -> 在实际故障位置尽快终止
+ -> 用于开发、调试和测试
+ -> 优先保留故障现场
+
+exception
+ -> 使用异常自然跨层传播
+ -> 中间层不处理
+ -> 到达上层故障隔离边界
+ -> 优先利用 task/thread/process 等隔离能力
+```
+
+`fast_fail` 只改变 Unknown Failure 的处置方式, **不得改变 Known Result / Unknown Failure 的分类。**
+
+## 4. 第三方库处理规则
+
+第三方库自身使用返回码还是异常,不决定本系统的处理方式。
+
+```text
+第三方返回结果
+ -> 本系统可预料、可处理
+ -> 转换为当前函数自己的返回码
+
+ -> 本系统没有可靠恢复方式
+ -> 转换为 Unknown Failure
+ -> 交给 Unknown Failure Policy
+```
+
+不得机械透传第三方错误模型,也不得因为第三方返回错误码,就强制在本系统继续使用错误码。
+
+## 5. 其他要求
+
+`noexcept` 只用于明确保证异常不会逃逸的函数。
+
+不得为了错误处理增加重复检查、兼容层、无意义 `try/catch`,也不得改变原函数的既有语义。
+
+**最终原则:每个函数独立定义自己的已知返回结果;Unknown Failure 使用可配置的 Failure
+Policy;第三方结果进入系统后重新按同一规则分类。**
diff --git a/Project_naming_conventions.md b/Project_naming_conventions.md
new file mode 100644
index 0000000..c6c047d
--- /dev/null
+++ b/Project_naming_conventions.md
@@ -0,0 +1,58 @@
+# 项目命名规范
+
+* 类型、结构体、枚举、Concept、类型别名:`Upper_Snake_Case`
+
+```cpp
+Scene_Base
+Frame_Request_Result
+Renderable_Id
+```
+
+* 函数、参数、局部变量、常量:`lower_snake_case`
+
+```cpp
+render_frame()
+frame_count
+default_capacity
+```
+
+* 成员变量:`lower_snake_case_`
+
+```cpp
+scene_
+pending_exception_
+```
+
+* 枚举值:`lower_snake_case`
+
+```cpp
+enum class Frame_Request_Result {
+ none,
+ cancelled,
+ renderer_unavailable
+};
+```
+
+* 模板类型参数:`Upper_Snake_Case`
+
+```cpp
+template
+```
+
+* 命名空间:`lower_snake_case`
+
+```cpp
+namespace renderive::render_3d
+```
+
+* 文件名与主要类型一致:`Upper_Snake_Case`
+
+```text
+Scene_Base.hpp
+Gpu_Completion_Service.cpp
+Low_Latency_Strategy.inl
+```
+
+* 内部实现统一使用 `detail` 命名空间。
+
+**总规则:类型大写下划线,值和函数小写下划线,成员变量末尾加 `_`。**
diff --git a/kernel/main.cmake b/kernel/main.cmake
index 876d694..9810ddb 100644
--- a/kernel/main.cmake
+++ b/kernel/main.cmake
@@ -1,5 +1,6 @@
include(${CMAKE_CURRENT_LIST_DIR}/cmake/rely.cmake)
set(Aethera_Kernel_dependencies aethera_kernel::taskflow global::magic_enum global::expected)
+set(Aethera_BUILD_TESTS TRUE)
if (Aethera_BUILD_TESTS)
set(Aethera_Kernel_test_targets)
list(APPEND Aethera_Kernel_dependencies global::GTest)
@@ -22,7 +23,6 @@ endif ()
set(Aethera_Kernel_source_dir "${CMAKE_CURRENT_LIST_DIR}/src/kernel")
append_glob_source(Aethera_Kernel_sources "${Aethera_Kernel_source_dir}")
add_library(Aethera_Kernel STATIC ${Aethera_Kernel_sources})
-add_library(Renderive::Kernel ALIAS Aethera_Kernel)
set_target_properties(Aethera_Kernel PROPERTIES EXPORT_NAME Kernel)
target_include_directories(Aethera_Kernel PUBLIC
"$"
@@ -35,21 +35,20 @@ target_link_libraries(Aethera_Kernel PUBLIC
tl::expected
)
target_link_libraries(Aethera_Kernel PRIVATE Threads::Threads)
-function(Aethera_stage_kernel_runtime target)
- if (WIN32)
- add_custom_command(TARGET "${target}" POST_BUILD
- COMMAND "${CMAKE_COMMAND}" -E copy_if_different
- "$"
- "$"
- COMMENT "Staging oneTBB runtime for ${target}"
- VERBATIM)
- endif ()
-endfunction()
if (MSVC)
- target_compile_options(Aethera_Kernel PRIVATE /utf-8)
+ target_compile_options(Aethera_Kernel PUBLIC /utf-8)
+endif ()
+if (Aethera_BUILD_TESTS)
+ set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/src/test")
+ append_glob_source(Aethera_Kernel_test_sources "${Aethera_Kernel_test_dir}")
+ add_executable(Aethera_Kernel_exe ${Aethera_Kernel_test_sources})
+ target_link_libraries(Aethera_Kernel_exe PRIVATE Aethera_Kernel GTest::gtest)
+ add_test(NAME Aethera_Kernel_exe COMMAND Aethera_Kernel_exe)
+ set_tests_properties(Aethera_Kernel_exe PROPERTIES
+ LABELS "Aethera_Kernel"
+ ENVIRONMENT "Aethera_ERROR_MODE=exception")
+ list(APPEND Aethera_Kernel_test_targets Aethera_Kernel_exe)
endif ()
-add_executable(Aethera_Kernel_exe "${CMAKE_CURRENT_LIST_DIR}/src/test/main.cpp")
-target_link_libraries(Aethera_Kernel_exe PRIVATE Aethera_Kernel)
install(TARGETS Aethera_Kernel
EXPORT RenderiveTargets
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
@@ -59,35 +58,6 @@ install(DIRECTORY "${Aethera_Kernel_source_dir}/renderive"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp" PATTERN "*.inl")
if (Aethera_BUILD_TESTS)
- set(Aethera_Kernel_test_dir "${CMAKE_CURRENT_LIST_DIR}/tests")
- append_glob_source(Aethera_Kernel_test_sources "${Aethera_Kernel_test_dir}")
- # Concrete 2D/3D scene tests belong to their owning modules. The former
- # header-only Scene_Context fixtures were removed with the Builder-based
- # scene API, so Kernel's suite must remain independently linkable.
- list(FILTER Aethera_Kernel_test_sources EXCLUDE REGEX
- "(Real_Time_Data_Test|Renderable_Base_Test|Color_Cache_Test|Renderable_Test|Scene_Base_Test|Scene_Test|Dynamic_Renderable_Lifecycle_Test|Render_Plan_Execution_Test|Scene_Inheritance_Test|Scene_Memory_Resource_Test|Scene_State_Observer_Test|Scene2D_Context_Test|Scene2D_Render_Order_Test|Scene3D_Context_Test|Threading_Contract_Test)\\.cpp$")
- foreach (Aethera_Kernel_test_source IN LISTS Aethera_Kernel_test_sources)
- if (NOT Aethera_Kernel_test_source MATCHES "\\.(c|cc|cpp|cxx)$")
- continue()
- endif ()
- file(RELATIVE_PATH Aethera_Kernel_test_name "${Aethera_Kernel_test_dir}" "${Aethera_Kernel_test_source}")
- string(REGEX REPLACE "\\.[^.]+$" "" Aethera_Kernel_test_name "${Aethera_Kernel_test_name}")
- string(MAKE_C_IDENTIFIER "${Aethera_Kernel_test_name}" Aethera_Kernel_test_name)
- set(Aethera_Kernel_test_target "Aethera_Kernel_${Aethera_Kernel_test_name}")
- add_executable("${Aethera_Kernel_test_target}" "${Aethera_Kernel_test_source}")
- target_include_directories("${Aethera_Kernel_test_target}" PRIVATE
- "${Aethera_Kernel_test_dir}"
- "${CMAKE_CURRENT_LIST_DIR}/../render_2D"
- "${CMAKE_CURRENT_LIST_DIR}/../render_3D"
- )
- target_link_libraries("${Aethera_Kernel_test_target}" PRIVATE Aethera_Kernel GTest::gtest_main)
- Aethera_stage_kernel_runtime("${Aethera_Kernel_test_target}")
- add_test(NAME "${Aethera_Kernel_test_target}" COMMAND "${Aethera_Kernel_test_target}")
- set_tests_properties("${Aethera_Kernel_test_target}" PROPERTIES
- LABELS "Aethera_Kernel"
- ENVIRONMENT "Aethera_ERROR_MODE=exception")
- list(APPEND Aethera_Kernel_test_targets "${Aethera_Kernel_test_target}")
- endforeach ()
add_custom_target(Aethera_Kernel_check
COMMAND "${CMAKE_CTEST_COMMAND}" --test-dir "${CMAKE_BINARY_DIR}"
-C "$" -L "^Aethera_Kernel$" --output-on-failure
diff --git a/kernel/src/kernel/base/core.hpp b/kernel/src/kernel/double_buffer/core.hpp
similarity index 69%
rename from kernel/src/kernel/base/core.hpp
rename to kernel/src/kernel/double_buffer/core.hpp
index 211ce80..85b63dc 100644
--- a/kernel/src/kernel/base/core.hpp
+++ b/kernel/src/kernel/double_buffer/core.hpp
@@ -19,16 +19,13 @@
#include
#include
namespace double_buffer {
-template
-concept Lockable = requires(T& lock) {
+template
+concept Lockable = requires(Lock& lock) {
{ lock.lock() } -> std::same_as;
{ lock.unlock() } -> std::same_as;
};
-template
-concept Prop_State = requires {
- requires std::default_initializable;
- requires std::assignable_from;
-};
+template
+concept Prop_State = std::default_initializable && std::assignable_from;
struct Pmr {
using Resource = std::pmr::memory_resource;
private:
@@ -40,9 +37,9 @@ public:
[[nodiscard]] Resource* resource() const noexcept {
return value;
}
- template
- std::pmr::polymorphic_allocator allocator() const noexcept {
- return std::pmr::polymorphic_allocator{value};
+ template
+ std::pmr::polymorphic_allocator allocator() const noexcept {
+ return std::pmr::polymorphic_allocator{value};
}
static Resource* default_resource() noexcept {
return std::pmr::get_default_resource();
@@ -106,14 +103,15 @@ struct Pinned {
Pinned(Pinned&&) = delete;
Pinned& operator=(Pinned&&) = delete;
};
-template
+// Double_Buffer 只负责交换 pending/current 两个视图,不复制数据;需要交换后同步基线的场景使用 Synchronized_Double_Buffer。
+template
struct Double_Buffer : Pinned {
using allocator_type = std::pmr::polymorphic_allocator;
private:
- std::tuple buf;
+ std::tuple buf;
public:
- T* pending;
- T* current;
+ Value* pending;
+ Value* current;
Double_Buffer() : Double_Buffer(std::allocator_arg, allocator_type{std::pmr::get_default_resource()}) {}
Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : buf(std::allocator_arg, allocator),
pending(&std::get<0>(buf)),
@@ -122,20 +120,47 @@ public:
std::swap(pending, current);
}
};
+enum class Buffer_Direction {
+ inward,
+ outward
+};
+/*
+ * 同步双缓冲保留 Double_Buffer 的交换语义,并在交换后把发布值复制回写入侧。
+ * inward: pending 是外部写入侧,current 是内部消费侧;outward: current 是内部写入侧,pending 是外部发布侧。
+ */
+template
+struct Synchronized_Double_Buffer : Double_Buffer {
+ using Base = Double_Buffer;
+ using allocator_type = typename Base::allocator_type;
+ Synchronized_Double_Buffer() = default;
+ Synchronized_Double_Buffer(std::allocator_arg_t, const allocator_type& allocator) : Base(std::allocator_arg, allocator) {}
+ void exchange() {
+ Base::exchange();
+ if constexpr (Direction == Buffer_Direction::inward) *this->pending = *this->current;
+ else *this->current = *this->pending;
+ }
+};
namespace detail {
-struct State_Root {};
+struct State_Root {
+ bool operator==(const State_Root&) const = default;
+};
}
-template
+// State_Type 用 Tag 标记每一层状态,Prev_State 把 CRTP 继承链上的状态按层串起来,供精确回调和类型约束使用。
+template
+requires Prop_State
struct State_Type : Prev {
using Tag_Type = Tag;
using Prev_State = Prev;
+ bool operator==(const State_Type&) const = default;
};
-template
+template
+requires Prop_State
struct Tagged_Buffer {
using Tag_Type = Tag;
- using Value_Type = T;
+ using Value_Type = Value;
};
struct Root;
+// Rely_Type 描述一类依赖图及其允许绑定的对象类型;dirty 只表示该依赖阶段需要重新处理,不承担图结构所有权。
template
struct Rely_Type {
using Tag_Type = Tag;
@@ -167,47 +192,45 @@ struct Unique_Types : std::bool_constant<
((!std::same_as) && ...) &&
Unique_Types::value
> {};
-template
+template
struct Is_Tagged_Buffer : std::false_type {};
-template
-struct Is_Tagged_Buffer> : std::true_type {};
-template
+template
+struct Is_Tagged_Buffer> : std::true_type {};
+template
struct Is_Rely_Type : std::false_type {};
template
struct Is_Rely_Type> : std::true_type {};
-template
+template
struct Is_State_Type : std::false_type {};
-template
+template
struct Is_State_Type> : std::true_type {};
-template
-concept Buffer_Type = Is_Tagged_Buffer::value;
-template
-concept Rely_Mechanism = Is_Rely_Type::value;
-template
-concept State_Mechanism = Is_State_Type::value;
-template
-concept Mechanism_Type = requires {
- requires Buffer_Type || Rely_Mechanism || State_Mechanism;
+template
+concept Buffer_Type = Is_Tagged_Buffer::value;
+template
+concept Rely_Mechanism = Is_Rely_Type::value;
+template
+concept State_Mechanism = Is_State_Type::value;
+template
+concept Mechanism_Type = Buffer_Type || Rely_Mechanism || State_Mechanism;
+template
+concept Tagged_State = Prop_State && requires {
+ typename Value::Tag_Type;
+ typename Value::Prev_State;
+ requires std::derived_from>;
};
-template
-concept Tagged_State = Prop_State && requires {
- typename T::Tag_Type;
- typename T::Prev_State;
- requires std::derived_from>;
-};
-template
+template
struct State_Layers {
using Type = std::tuple<>;
};
-template
-struct State_Layers {
+template
+struct State_Layers> {
using Type = decltype(std::tuple_cat(
- std::declval>(),
- std::declval::Type>()
+ std::declval>(),
+ std::declval::Type>()
));
};
-template
-using State_Layers_T = typename State_Layers::Type;
+template
+using State_Layers_T = typename State_Layers::Type;
template
struct Member_Pointer_Traits;
template
@@ -228,11 +251,11 @@ concept State_Member_Settable = requires(State& state, Value&& value) {
requires State_Member;
state.*Member = std::forward(value);
};
-template
+template
using Mechanism_Buffer_Tuple = std::conditional_t, std::tuple, std::tuple<>>;
-template
+template
using Mechanism_Rely_Tuple = std::conditional_t, std::tuple, std::tuple<>>;
-template
+template
using Mechanism_State_Tuple = std::conditional_t, std::tuple, std::tuple<>>;
template
struct Has_Tag : std::false_type {};
@@ -274,9 +297,9 @@ concept Rely_Tag_In = requires {
requires Rely_List;
requires Has_Tag::value;
};
-template
+template
struct Rely_Type_By_Tag;
-template
+template
struct Rely_Type_By_Tag> {
private:
template
@@ -294,9 +317,9 @@ private:
public:
using Type = typename Find::Type;
};
-template requires Rely_Tag_In
+template
using Rely_Declaration = typename Rely_Type_By_Tag::Type;
-template requires Rely_Tag_In
+template
using Rely_Bound_Object = typename Rely_Declaration::Object_Type;
template
struct Is_State_List : std::false_type {};
@@ -307,16 +330,16 @@ struct Is_State_List> : Tagged_List_Check<
> {};
template
concept State_List = Is_State_List::value;
-template
-concept State_Chain = Tagged_State && State_List>;
+template
+concept State_Chain = Tagged_State && State_List>;
template
concept State_Tag_In = requires {
requires State_List;
requires Has_Tag::value;
};
-template
-concept State_Chain_Matches = State_Chain && State_List && [] {
- using Layers = State_Layers_T;
+template
+concept State_Chain_Matches = State_Chain && State_List && [] {
+ using Layers = State_Layers_T;
if constexpr (std::tuple_size_v != std::tuple_size_v) return false;
else {
return [](std::index_sequence) {
@@ -327,86 +350,84 @@ concept State_Chain_Matches = State_Chain && State_List && [] {
}(std::make_index_sequence>{});
}
}();
-template
+template
struct Rebind_State;
-template
+template
struct Rebind_State, Prev> {
using Type = State_Type;
};
-template
-using Rebind_State_T = typename Rebind_State::Type;
-template
-consteval std::size_t state_tag_index() {
- std::size_t result{};
- std::size_t current{};
- ((std::same_as ? result = current : result, ++current), ...);
- return result;
+template
+using Rebind_State_T = typename Rebind_State::Type;
+// Tag 是否存在由外层 requires 保证;内部只按声明顺序计算位置,避免每层重复检查,也避开 MSVC 对复杂 fold 表达式的解析问题。
+template
+consteval std::size_t tag_index() {
+ if constexpr (std::is_same_v) return 0;
+ else if constexpr (sizeof...(Rest) == 0) return 0;
+ else return 1 + tag_index();
}
-template
-struct State_By_Tag;
-template
-struct State_By_Tag {
+template
+struct Tag_Index;
+template
+struct Tag_Index> : std::integral_constant()> {};
+template
+inline constexpr std::size_t tag_index_v = Tag_Index::value;
+template
+struct State_By_Tag {
private:
- using Layers = State_Layers_T;
- template
- static consteval std::size_t index(std::index_sequence) {
- std::size_t result{};
- ((void)(std::same_as::Tag_Type> ? result = I : result), ...);
- return result;
- }
+ using Layers = State_Layers_T;
public:
- using Type = std::tuple_element_t>{}), Layers>;
+ using Type = std::tuple_element_t, Layers>;
};
-template
-using State_Value = typename State_By_Tag::Type;
-template
-concept State_Callback_For = State_Tag_In && State_Chain_Matches && std::invocable&>;
+template
+using State_Value = typename State_By_Tag::Type;
+template
+concept State_Callback_For = State_Tag_In && State_Chain_Matches && std::invocable&>;
template
-struct State_Callback_Storage_Impl;
-template
-struct State_Callback_Storage_Impl> {
+struct State_Callback_Tuple;
+template
+struct State_Callback_Tuple> {
+ using Type = std::tuple...>;
+};
+template
+struct State_Callback_Storage {
private:
- std::tuple...> callbacks;
+ using Layers = State_Layers_T;
+ using Callbacks = typename State_Callback_Tuple::Type;
+ Callbacks callbacks;
+ template
+ static consteval std::size_t index() {
+ return tag_index_v;
+ }
public:
- template requires
- (std::same_as || ...)
+ template requires State_Tag_In
void set(Callback&& callback) {
- constexpr auto index = state_tag_index();
- using State = std::tuple_element_t>;
- std::get(callbacks) = std::function{std::forward(callback)};
+ constexpr auto value_index = index();
+ using Layer = std::tuple_element_t;
+ std::get(callbacks) = std::function{std::forward(callback)};
}
- template requires (std::same_as || ...)
+ template requires State_Tag_In
void clear() {
- constexpr auto index = state_tag_index();
- std::get(callbacks) = {};
+ std::get()>(callbacks) = {};
}
- template requires (std::same_as || ...)
- void notify(const State& state) {
- constexpr auto index = state_tag_index();
- using Layer = std::tuple_element_t>;
- auto& callback = std::get(callbacks);
+ template requires State_Tag_In
+ void notify(const State_T& state) {
+ constexpr auto value_index = index();
+ using Layer = std::tuple_element_t;
+ auto& callback = std::get(callbacks);
if (callback) callback(static_cast(state));
}
};
-template
-using State_Callback_Storage = State_Callback_Storage_Impl>;
-template
-consteval std::size_t tag_index() {
- std::size_t result{};
- std::size_t current{};
- ((std::same_as ? result = current : result, ++current), ...);
- return result;
-}
-template
+// 每个 State Tag 独立保存回调;字段更新不会隐式通知,发布频率由拥有该状态的内部机制显式调用 notify 控制。
+template
struct Buffer_Value_By_Tag;
-template
+template