修复大错误移动到prop
This commit is contained in:
@@ -157,6 +157,16 @@ public:
|
||||
void disconnect(Root* object) {
|
||||
edit_dependency_graph->disconnect_impl(object);
|
||||
}
|
||||
/* 添加 Prop 字段级依赖;source 通过 set 修改该字段时目标阶段变脏。 */
|
||||
template <auto Member, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Prop_Dependency_Source<Member> Source>
|
||||
Node* add_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(target, source, detail::dependency_id<detail::Prop_Dependency_Key<Member>>(), target_tag, target_dirty_key, bind_node_data);
|
||||
}
|
||||
/* 添加 Prop 声明层级依赖;Owner 声明的任一字段通过 set 修改时目标阶段变脏。 */
|
||||
template <typename Prop_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Prop_Layer_Dependency_Source<Prop_Tag> Source>
|
||||
Node* add_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(target, source, detail::dependency_id<detail::Prop_Layer_Dependency_Key<Prop_Tag>>(), target_tag, target_dirty_key, bind_node_data);
|
||||
}
|
||||
template <auto Member, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::State_Dependency_Source<Member> Source>
|
||||
Node* add_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->template add_dependency_runtime<Bound_Object, Target, Source>(
|
||||
@@ -206,6 +216,14 @@ public:
|
||||
bool remove_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->remove_edge(target, source, detail::dependency_id<detail::State_Dependency_Key<Member>>(), target_tag);
|
||||
}
|
||||
template <auto Member, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Prop_Dependency_Source<Member> Source>
|
||||
bool remove_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->remove_edge(target, source, detail::dependency_id<detail::Prop_Dependency_Key<Member>>(), target_tag);
|
||||
}
|
||||
template <typename Prop_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::Prop_Layer_Dependency_Source<Prop_Tag> Source>
|
||||
bool remove_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph->remove_edge(target, source, detail::dependency_id<detail::Prop_Layer_Dependency_Key<Prop_Tag>>(), target_tag);
|
||||
}
|
||||
/* 移除指定 State Tag 的状态层级依赖,不影响同对象上的字段级依赖。 */
|
||||
template <typename State_Tag, detail::Bound_Dependency_Graph_Target<Bound_Object> Target, detail::State_Layer_Dependency_Source<State_Tag> Source>
|
||||
bool remove_state_dependency(Target* target, Source* source) {
|
||||
|
||||
@@ -286,6 +286,14 @@ struct Root::Builder {
|
||||
}
|
||||
);
|
||||
}
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Prop_Dependency_Source<Member> Source>
|
||||
Builder& add_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph<Dependency_Graph_Tag>([&](auto& editor) { editor.template add_prop_dependency<Member>(target, source); });
|
||||
}
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename Prop_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Prop_Layer_Dependency_Source<Prop_Tag> Source>
|
||||
Builder& add_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph<Dependency_Graph_Tag>([&](auto& editor) { editor.template add_prop_dependency<Prop_Tag>(target, source); });
|
||||
}
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::State_Dependency_Source<Member> Source>
|
||||
Builder& add_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph<Dependency_Graph_Tag>(
|
||||
@@ -327,6 +335,14 @@ struct Root::Builder {
|
||||
}
|
||||
);
|
||||
}
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, auto Member, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Prop_Dependency_Source<Member> Source>
|
||||
Builder& remove_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph<Dependency_Graph_Tag>([&](auto& editor) { editor.template remove_prop_dependency<Member>(target, source); });
|
||||
}
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename Prop_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::Prop_Layer_Dependency_Source<Prop_Tag> Source>
|
||||
Builder& remove_prop_dependency(Target* target, Source* source) {
|
||||
return edit_dependency_graph<Dependency_Graph_Tag>([&](auto& editor) { editor.template remove_prop_dependency<Prop_Tag>(target, source); });
|
||||
}
|
||||
/* 从构造期依赖图中移除指定 State Tag 的状态层级依赖。 */
|
||||
template <detail::Dependency_Graph_Tag_In<typename Object::Dependency_Graph_Types> Dependency_Graph_Tag, typename State_Tag, detail::Bound_Dependency_Graph_Target<detail::Dependency_Graph_Bound_Object<Dependency_Graph_Tag, typename Object::Dependency_Graph_Types>> Target, detail::State_Layer_Dependency_Source<State_Tag> Source>
|
||||
Builder& remove_state_dependency(Target* target, Source* source) {
|
||||
|
||||
@@ -145,10 +145,19 @@ struct Publish_Double_Buffer : Double_Buffer<Value> {
|
||||
}
|
||||
};
|
||||
namespace detail {
|
||||
struct Prop_Root {
|
||||
bool operator==(const Prop_Root&) const = default;
|
||||
};
|
||||
struct State_Root {
|
||||
bool operator==(const State_Root&) const = default;
|
||||
};
|
||||
}
|
||||
template <typename Tag, Prop_State Prev = detail::Prop_Root>
|
||||
struct Prop_Type : Prev {
|
||||
using Tag_Type = Tag;
|
||||
using Prev_Prop = Prev;
|
||||
bool operator==(const Prop_Type&) const = default;
|
||||
};
|
||||
// State_Type 用 Tag 标记每一层状态,Prev_State 把 CRTP 继承链上的状态按层串起来,供精确回调和类型约束使用。
|
||||
template <typename Tag, Prop_State Prev = detail::State_Root>
|
||||
struct State_Type : Prev {
|
||||
@@ -207,19 +216,27 @@ struct Is_State_Type : std::false_type {};
|
||||
template <typename Tag, typename Prev>
|
||||
struct Is_State_Type<State_Type<Tag, Prev>> : std::true_type {};
|
||||
template <typename Value>
|
||||
struct Is_Prop_Type : std::false_type {};
|
||||
template <typename Tag, typename Prev>
|
||||
struct Is_Prop_Type<Prop_Type<Tag, Prev>> : std::true_type {};
|
||||
template <typename Value>
|
||||
concept Buffer_Type = Is_Tagged_Buffer<Value>::value;
|
||||
template <typename Value>
|
||||
concept Dependency_Graph_Mechanism = Is_Dependency_Graph_Type<Value>::value;
|
||||
template <typename Value>
|
||||
concept State_Mechanism = Is_State_Type<Value>::value;
|
||||
template <typename Value>
|
||||
concept Mechanism_Type = Buffer_Type<Value> || Dependency_Graph_Mechanism<Value> || State_Mechanism<Value>;
|
||||
concept Mechanism_Type = Buffer_Type<Value> || Dependency_Graph_Mechanism<Value>;
|
||||
template <typename Value>
|
||||
concept Tagged_State = Prop_State<Value> && requires {
|
||||
typename Value::Tag_Type;
|
||||
typename Value::Prev_State;
|
||||
requires std::derived_from<Value, State_Type<typename Value::Tag_Type, typename Value::Prev_State>>;
|
||||
};
|
||||
template <typename Value>
|
||||
concept Tagged_Prop = Prop_State<Value> && requires {
|
||||
typename Value::Tag_Type;
|
||||
typename Value::Prev_Prop;
|
||||
requires std::derived_from<Value, Prop_Type<typename Value::Tag_Type, typename Value::Prev_Prop>>;
|
||||
};
|
||||
template <typename State_T, typename = void>
|
||||
struct State_Layers {
|
||||
using Type = std::tuple<>;
|
||||
@@ -233,6 +250,16 @@ struct State_Layers<State_T, std::void_t<typename State_T::Tag_Type, typename St
|
||||
};
|
||||
template <typename State_T>
|
||||
using State_Layers_T = typename State_Layers<State_T>::Type;
|
||||
template <typename Prop_T, typename = void>
|
||||
struct Prop_Layers {
|
||||
using Type = std::tuple<>;
|
||||
};
|
||||
template <typename Prop_T>
|
||||
struct Prop_Layers<Prop_T, std::void_t<typename Prop_T::Tag_Type, typename Prop_T::Prev_Prop>> {
|
||||
using Type = decltype(std::tuple_cat(std::declval<std::tuple<Prop_T>>(), std::declval<typename Prop_Layers<typename Prop_T::Prev_Prop>::Type>()));
|
||||
};
|
||||
template <typename Prop_T>
|
||||
using Prop_Layers_T = typename Prop_Layers<Prop_T>::Type;
|
||||
template <typename T>
|
||||
struct Member_Pointer_Traits;
|
||||
template <typename Member, typename Owner>
|
||||
@@ -250,6 +277,14 @@ concept State_Member = requires {
|
||||
};
|
||||
template <auto Member>
|
||||
using State_Member_Tag = typename Member_Pointer_Traits<decltype(Member)>::Owner_Type::Tag_Type;
|
||||
template <auto Member, typename Prop>
|
||||
concept Prop_Member = requires {
|
||||
requires std::is_member_object_pointer_v<decltype(Member)>;
|
||||
requires std::derived_from<Prop, typename Member_Pointer_Traits<decltype(Member)>::Owner_Type>;
|
||||
requires Tagged_Prop<typename Member_Pointer_Traits<decltype(Member)>::Owner_Type>;
|
||||
};
|
||||
template <auto Member>
|
||||
using Prop_Member_Tag = typename Member_Pointer_Traits<decltype(Member)>::Owner_Type::Tag_Type;
|
||||
template <typename Value, auto Member, typename State>
|
||||
concept State_Member_Settable = requires(State& state, Value&& value) {
|
||||
requires State_Member<Member, State>;
|
||||
@@ -259,8 +294,6 @@ template <typename T>
|
||||
using Mechanism_Buffer_Tuple = std::conditional_t<Buffer_Type<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <typename T>
|
||||
using Mechanism_Dependency_Graph_Tuple = std::conditional_t<Dependency_Graph_Mechanism<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <typename T>
|
||||
using Mechanism_State_Tuple = std::conditional_t<State_Mechanism<T>, std::tuple<T>, std::tuple<>>;
|
||||
template <typename Tag, typename Tuple>
|
||||
struct Has_Tag : std::false_type {};
|
||||
template <typename Tag, typename... Types>
|
||||
@@ -334,6 +367,19 @@ struct Is_State_List<std::tuple<States...>> : Tagged_List_Check<
|
||||
> {};
|
||||
template <typename Tuple>
|
||||
concept State_List = Is_State_List<Tuple>::value;
|
||||
template <typename Tuple>
|
||||
struct Is_Prop_List : std::false_type {};
|
||||
template <typename... Props>
|
||||
struct Is_Prop_List<std::tuple<Props...>> : Tagged_List_Check<(Tagged_Prop<Props> && ...), Props...> {};
|
||||
template <typename Tuple>
|
||||
concept Prop_List = Is_Prop_List<Tuple>::value;
|
||||
template <typename Prop_T>
|
||||
concept Prop_Chain = Tagged_Prop<Prop_T> && Prop_List<Prop_Layers_T<Prop_T>>;
|
||||
template <typename Tag, typename Tuple>
|
||||
concept Prop_Tag_In = requires {
|
||||
requires Prop_List<Tuple>;
|
||||
requires Has_Tag<Tag, Tuple>::value;
|
||||
};
|
||||
template <typename State_T>
|
||||
concept State_Chain = Tagged_State<State_T> && State_List<State_Layers_T<State_T>>;
|
||||
template <typename Tag, typename Tuple>
|
||||
@@ -384,6 +430,15 @@ public:
|
||||
};
|
||||
template <typename Tag, typename State_T>
|
||||
using State_Value = typename State_By_Tag<Tag, State_T>::Type;
|
||||
template <typename Tag, typename Prop_T>
|
||||
struct Prop_By_Tag {
|
||||
private:
|
||||
using Layers = Prop_Layers_T<Prop_T>;
|
||||
public:
|
||||
using Type = std::tuple_element_t<tag_index_v<Tag, Layers>, Layers>;
|
||||
};
|
||||
template <typename Tag, typename Prop_T>
|
||||
using Prop_Value = typename Prop_By_Tag<Tag, Prop_T>::Type;
|
||||
template <typename Callback, typename Tag, typename State_T, typename States>
|
||||
concept State_Callback_For = State_Tag_In<Tag, States> && State_Chain_Matches<State_T, States> && std::invocable<Callback, const State_Value<Tag, State_T>&>;
|
||||
template <typename Tuple>
|
||||
@@ -522,8 +577,53 @@ public:
|
||||
};
|
||||
template <typename State_Type> requires detail::State_Chain<std::remove_const_t<State_Type>>
|
||||
State_Access(State_Type&) -> State_Access<State_Type>;
|
||||
/* Prop_Access 按定义层的 Base_Tag 选择 Prop;Prop 与 State 的标签空间彼此隔离。 */
|
||||
template <typename Prop_Type> requires detail::Prop_Chain<std::remove_const_t<Prop_Type>>
|
||||
class Prop_Access {
|
||||
private:
|
||||
using Value_Type = std::remove_const_t<Prop_Type>;
|
||||
Prop_Type* prop;
|
||||
template <typename Other_Prop_Type> requires detail::Prop_Chain<std::remove_const_t<Other_Prop_Type>>
|
||||
friend class Prop_Access;
|
||||
public:
|
||||
explicit Prop_Access(Prop_Type& value) noexcept : prop(&value) {}
|
||||
template <typename Source_Prop_Type> requires std::convertible_to<Source_Prop_Type*, Prop_Type*>
|
||||
Prop_Access(const Prop_Access<Source_Prop_Type>& source) noexcept : prop(source.prop) {}
|
||||
template <typename Tag> requires detail::Prop_Tag_In<Tag, detail::Prop_Layers_T<Value_Type>>
|
||||
[[nodiscard]] decltype(auto) get() const noexcept {
|
||||
using Layer = detail::Prop_Value<Tag, Value_Type>;
|
||||
if constexpr (std::is_const_v<Prop_Type>) return static_cast<const Layer&>(*prop);
|
||||
else return static_cast<Layer&>(*prop);
|
||||
}
|
||||
};
|
||||
template <typename Prop_Type> requires detail::Prop_Chain<std::remove_const_t<Prop_Type>>
|
||||
Prop_Access(Prop_Type&) -> Prop_Access<Prop_Type>;
|
||||
/* Private_Access 使用同一个定义层 Base_Tag 选择 Private;该标签空间不与 Prop/State 混用。 */
|
||||
template <typename Private_Type>
|
||||
class Private_Access {
|
||||
private:
|
||||
Private_Type* private_data;
|
||||
template <typename Other_Private_Type>
|
||||
friend class Private_Access;
|
||||
public:
|
||||
explicit Private_Access(Private_Type& value) noexcept : private_data(&value) {}
|
||||
template <typename Source_Private_Type> requires std::convertible_to<Source_Private_Type*, Private_Type*>
|
||||
Private_Access(const Private_Access<Source_Private_Type>& source) noexcept : private_data(source.private_data) {}
|
||||
template <typename Tag> requires requires { typename Tag::Private; } && std::derived_from<std::remove_const_t<Private_Type>, typename Tag::Private>
|
||||
[[nodiscard]] decltype(auto) get() const noexcept {
|
||||
using Layer = typename Tag::Private;
|
||||
if constexpr (std::is_const_v<Private_Type>) return static_cast<const Layer&>(*private_data);
|
||||
else return static_cast<Layer&>(*private_data);
|
||||
}
|
||||
};
|
||||
template <typename Private_Type>
|
||||
Private_Access(Private_Type&) -> Private_Access<Private_Type>;
|
||||
namespace detail {
|
||||
template <auto Member>
|
||||
struct Prop_Dependency_Key {};
|
||||
template <typename Tag>
|
||||
struct Prop_Layer_Dependency_Key {};
|
||||
template <auto Member>
|
||||
struct State_Dependency_Key {};
|
||||
template <typename Tag>
|
||||
struct State_Layer_Dependency_Key {};
|
||||
@@ -563,10 +663,10 @@ enum class Dependency_Graph_Error {
|
||||
missing_dependency,
|
||||
cycle
|
||||
};
|
||||
struct Root_State_Tag {};
|
||||
struct Root {
|
||||
/* 所有实现层 Private 的公共析构基类;Root 通过该类型唯一拥有最终 Private。 */
|
||||
struct Private {
|
||||
using Tag_Type = Root;
|
||||
virtual ~Private() = default;
|
||||
/* CRTP 默认:Builder 挂接最终 Private 后按基类到派生类绑定最终对象类型;Root 层不处理。 */
|
||||
template <typename Object>
|
||||
@@ -574,9 +674,12 @@ struct Root {
|
||||
};
|
||||
using Buffers = std::tuple<>;
|
||||
using Dependency_Graph_Types = std::tuple<>;
|
||||
using States = std::tuple<State_Type<Root_State_Tag>>;
|
||||
struct Prop {};
|
||||
struct State : State_Type<Root_State_Tag> {
|
||||
using Base_Tag = Root;
|
||||
using States = std::tuple<State_Type<Base_Tag>>;
|
||||
struct Prop : Prop_Type<Base_Tag> {
|
||||
bool operator==(const Prop&) const = default;
|
||||
};
|
||||
struct State : State_Type<Base_Tag> {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
private:
|
||||
@@ -666,8 +769,10 @@ namespace detail {
|
||||
template <typename T>
|
||||
concept Dependency_Object = Attached<T> && requires {
|
||||
requires Root_Derived<T>;
|
||||
typename T::Prop;
|
||||
typename T::State;
|
||||
typename T::Buffers;
|
||||
requires Prop_State<typename T::Prop>;
|
||||
requires Prop_State<typename T::State>;
|
||||
requires Buffer_List<typename T::Buffers>;
|
||||
};
|
||||
@@ -680,6 +785,16 @@ concept Bound_Dependency_Graph_Target = Bound_Dependency_Object<T> && std::deriv
|
||||
template <typename T, typename Bound>
|
||||
concept Dependency_Graph_Target = Root_Derived<T> && std::derived_from<T, Bound>;
|
||||
template <typename Source, auto Member>
|
||||
concept Prop_Dependency_Source = requires {
|
||||
requires Dependency_Object<Source>;
|
||||
requires Prop_Member<Member, typename Source::Prop>;
|
||||
};
|
||||
template <typename Source, typename Tag>
|
||||
concept Prop_Layer_Dependency_Source = requires {
|
||||
requires Dependency_Object<Source>;
|
||||
requires Prop_Tag_In<Tag, Prop_Layers_T<typename Source::Prop>>;
|
||||
};
|
||||
template <typename Source, auto Member>
|
||||
concept State_Dependency_Source = requires {
|
||||
requires Dependency_Object<Source>;
|
||||
requires State_Member<Member, typename Source::State>;
|
||||
|
||||
@@ -6,18 +6,20 @@ concept Object = requires {
|
||||
requires Object_Root<T>;
|
||||
typename T::This_Object;
|
||||
typename T::Prev_Object;
|
||||
typename T::Base_Tag;
|
||||
typename T::Prev_Prop;
|
||||
typename T::State_Tag;
|
||||
typename T::template Prev_State<typename T::State_Tag>;
|
||||
typename T::Prev_State;
|
||||
typename T::template Prev_Builder<T>;
|
||||
typename T::Prev_Private;
|
||||
typename T::States;
|
||||
requires std::same_as<typename T::This_Object, T>;
|
||||
requires std::same_as<typename T::Base_Tag, T>;
|
||||
requires Object_Root<typename T::Prev_Object>;
|
||||
requires std::derived_from<typename T::Prop, typename T::Prev_Prop>;
|
||||
requires detail::State_Tag_In<typename T::State_Tag, typename T::States>;
|
||||
requires detail::State_Chain<typename T::template Prev_State<typename T::State_Tag>>;
|
||||
requires std::derived_from<typename T::State, typename T::template Prev_State<typename T::State_Tag>>;
|
||||
requires detail::Tagged_Prop<typename T::Prop>;
|
||||
requires detail::State_Tag_In<typename T::Base_Tag, typename T::States>;
|
||||
requires detail::State_Chain<typename T::Prev_State>;
|
||||
requires std::derived_from<typename T::State, typename T::Prev_State>;
|
||||
requires detail::State_Chain_Matches<typename T::State, typename T::States>;
|
||||
requires std::derived_from<typename T::template Builder<T>, typename T::template Prev_Builder<T>>;
|
||||
requires std::derived_from<typename T::Private, typename T::Prev_Private>;
|
||||
@@ -33,26 +35,16 @@ using Impl_Dependency_Graph_Types = decltype(std::tuple_cat(
|
||||
std::declval<typename Base::Dependency_Graph_Types>(),
|
||||
std::declval<Mechanism_Dependency_Graph_Tuple<Local_Mechanisms>>()...
|
||||
));
|
||||
template <typename... Local_Mechanisms>
|
||||
using Local_States = decltype(std::tuple_cat(
|
||||
std::declval<Mechanism_State_Tuple<Local_Mechanisms>>()...
|
||||
));
|
||||
template <typename Base, typename... Local_Mechanisms>
|
||||
template <typename Self, typename Base>
|
||||
using Impl_States = decltype(std::tuple_cat(
|
||||
std::declval<typename Base::States>(),
|
||||
std::declval<Local_States<Local_Mechanisms...>>()
|
||||
std::declval<std::tuple<State_Type<Self>>>()
|
||||
));
|
||||
template <typename Base, typename... Local_Mechanisms>
|
||||
using Impl_State_Base = Rebind_State_T<
|
||||
std::tuple_element_t<0, Local_States<Local_Mechanisms...>>,
|
||||
typename Base::State
|
||||
>;
|
||||
template <typename Base, typename... Local_Mechanisms>
|
||||
template <typename Self, typename Base, typename... Local_Mechanisms>
|
||||
concept Impl_Mechanisms = Object_Root<Base> && (Mechanism_Type<Local_Mechanisms> && ...) && requires {
|
||||
requires std::tuple_size_v<Local_States<Local_Mechanisms...>> == 1;
|
||||
requires Buffer_List<Impl_Buffers<Base, Local_Mechanisms...>>;
|
||||
requires Dependency_Graph_List<Impl_Dependency_Graph_Types<Base, Local_Mechanisms...>>;
|
||||
requires State_List<Impl_States<Base, Local_Mechanisms...>>;
|
||||
requires State_List<Impl_States<Self, Base>>;
|
||||
};
|
||||
template <typename Callback, typename Private, typename Object_T>
|
||||
concept Process_Callback_For = requires(Private& private_data, Object_T* object, Callback&& callback) {
|
||||
@@ -61,27 +53,33 @@ concept Process_Callback_For = requires(Private& private_data, Object_T* object,
|
||||
}
|
||||
// Impl 在编译期把 Buffer/Dependency_Graph/State 三类机制叠加到继承链;每一层只声明自己的机制,最终类型汇总完整能力。
|
||||
template <typename Self, Object_Root Base, detail::Mechanism_Type... Local_Mechanisms> requires
|
||||
detail::Impl_Mechanisms<Base, Local_Mechanisms...>
|
||||
detail::Impl_Mechanisms<Self, Base, Local_Mechanisms...>
|
||||
struct Def : Base {
|
||||
using This_Object = Self;
|
||||
using Prev_Object = Base;
|
||||
using Prev_Prop = typename Base::Prop;
|
||||
using Base_Tag = Self;
|
||||
using Prev_Prop = Prop_Type<Base_Tag, typename Base::Prop>;
|
||||
template <typename Object>
|
||||
using Prev_Builder = typename Base::template Builder<Object>;
|
||||
using State_Declaration = std::tuple_element_t<0, detail::Local_States<Local_Mechanisms...>>;
|
||||
using State_Tag = typename State_Declaration::Tag_Type;
|
||||
template <std::same_as<State_Tag> Tag>
|
||||
using Prev_State = detail::Rebind_State_T<State_Declaration, typename Base::State>;
|
||||
using Prev_State = State_Type<Base_Tag, typename Base::State>;
|
||||
using Base_Private = typename Base::Private;
|
||||
using Buffers = detail::Impl_Buffers<Base, Local_Mechanisms...>;
|
||||
using Dependency_Graph_Types = detail::Impl_Dependency_Graph_Types<Base, Local_Mechanisms...>;
|
||||
using States = detail::Impl_States<Base, Local_Mechanisms...>;
|
||||
using States = detail::Impl_States<Self, Base>;
|
||||
struct Private : Base_Private {
|
||||
using Tag_Type = Base_Tag;
|
||||
using Prev_Private = Base_Private;
|
||||
/* CRTP 默认:继续调用上一 Private 层的最终对象绑定;派生 Private 覆盖时必须先调用此实现。 */
|
||||
template <typename Object>
|
||||
void bind_private_crtp(Object* object) {
|
||||
Base_Private::bind_private_crtp(object);
|
||||
}
|
||||
/* CRTP 可覆盖:写入 Prop 成员前按基类到派生类顺序调用。 */
|
||||
template <typename Owner, typename Member, typename Prop_T>
|
||||
void before_prop_set(Self* object, Member Owner::* member, Prop_Access<Prop_T> props) {}
|
||||
/* CRTP 可覆盖:写入 Prop 成员后按派生类到基类顺序调用。 */
|
||||
template <typename Owner, typename Member, typename Prop_T>
|
||||
void after_prop_set(Self* object, Member Owner::* member, Prop_Access<Prop_T> props) {}
|
||||
/* CRTP 可覆盖:写入 State 成员前按基类到派生类顺序调用;pending_states.get<Tag>() 返回对应可写状态层。 */
|
||||
template <typename Owner, typename Member, typename State_T>
|
||||
void before_state_set(Self* object, Member Owner::* member, State_Access<State_T> pending_states) {}
|
||||
@@ -157,6 +155,24 @@ private:
|
||||
}
|
||||
}
|
||||
template <typename Owner, typename Member>
|
||||
void before_prop_set(Member Owner::* member) {
|
||||
Prop_Access props{*data().current};
|
||||
auto callback = [&](auto& private_data) { private_data.before_prop_set(this, member, props); };
|
||||
walk_private<false, Obj>(callback);
|
||||
}
|
||||
template <typename Owner, typename Member>
|
||||
void after_prop_set(Member Owner::* member) {
|
||||
Prop_Access props{*data().current};
|
||||
auto callback = [&](auto& private_data) { private_data.after_prop_set(this, member, props); };
|
||||
walk_private<true, Obj>(callback);
|
||||
}
|
||||
template <auto Member>
|
||||
void emit_prop_dependencies() {
|
||||
this->emit_dependency_source(detail::dependency_id<detail::Prop_Dependency_Key<Member>>());
|
||||
using Prop_Tag = detail::Prop_Member_Tag<Member>;
|
||||
this->emit_dependency_source(detail::dependency_id<detail::Prop_Layer_Dependency_Key<Prop_Tag>>());
|
||||
}
|
||||
template <typename Owner, typename Member>
|
||||
void before_state_set(Member Owner::* member) {
|
||||
State_Access pending_states{*data().state.pending};
|
||||
auto callback = [&](auto& private_data) {
|
||||
@@ -267,12 +283,36 @@ public:
|
||||
void for_each_current_dependency_graph(Callback&& callback) const {
|
||||
data().dependency_graph_storage.for_each_current(std::forward<Callback>(callback));
|
||||
}
|
||||
template <typename Owner, typename Member, detail::Prop_Member_Settable<Prop, Owner, Member> Value>
|
||||
Impl& set(Member Owner::* member, Value&& value) {
|
||||
template <auto Member, typename Value> requires detail::Prop_Member<Member, Prop> && requires(Prop& prop, Value&& value) { prop.*Member = std::forward<Value>(value); }
|
||||
Impl& set(Value&& value) {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
data().current->*member = std::forward<Value>(value);
|
||||
before_prop_set(Member);
|
||||
data().current->*Member = std::forward<Value>(value);
|
||||
after_prop_set(Member);
|
||||
emit_prop_dependencies<Member>();
|
||||
return *this;
|
||||
}
|
||||
template <auto Member> requires detail::Prop_Dependency_Source<Impl, Member>
|
||||
[[nodiscard]] auto get() const {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
return data().current->*Member;
|
||||
}
|
||||
template <typename Tag> requires detail::Prop_Tag_In<Tag, detail::Prop_Layers_T<Prop>>
|
||||
[[nodiscard]] const auto& read_prop() const noexcept {
|
||||
using Layer = detail::Prop_Value<Tag, Prop>;
|
||||
return static_cast<const Layer&>(*data().current);
|
||||
}
|
||||
template <auto... Members, typename Callback> requires
|
||||
(sizeof...(Members) > 0) &&
|
||||
(detail::Prop_Member<Members, Prop> && ...) &&
|
||||
std::invocable<Callback, Prop_Access<Prop>>
|
||||
void update_prop(Callback&& callback) {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
(before_prop_set(Members), ...);
|
||||
std::invoke(std::forward<Callback>(callback), Prop_Access{*data().current});
|
||||
(after_prop_set(Members), ...);
|
||||
(emit_prop_dependencies<Members>(), ...);
|
||||
}
|
||||
template <detail::State_Tag_In<States> Tag, detail::State_Callback_For<Tag, State, States> Callback>
|
||||
void set_state_callback(Callback&& callback) {
|
||||
std::lock_guard<Lock> guard(lock);
|
||||
|
||||
@@ -16,6 +16,7 @@ using double_buffer::Def;
|
||||
using double_buffer::Impl;
|
||||
using double_buffer::State_Type;
|
||||
using double_buffer::State_Access;
|
||||
using double_buffer::Prop_Access;
|
||||
using double_buffer::Pmr;
|
||||
using double_buffer::Root;
|
||||
using double_buffer::Tagged_Buffer;
|
||||
|
||||
@@ -7,7 +7,6 @@ struct Color_Cache {
|
||||
virtual ~Color_Cache() = default;
|
||||
};
|
||||
/* Renderable 状态标签,用于访问和订阅 Renderable::State。 */
|
||||
struct Renderable_State_Tag {};
|
||||
struct Renderable;
|
||||
/* 最终 Private 提供 handle_event(T*, const Event&) 时具备事件处理能力。 */
|
||||
template <typename T>
|
||||
@@ -28,8 +27,8 @@ concept Prepare_Data_Renderable = Attached<T> && requires(typename T::Private& p
|
||||
* 子图首次执行前一定构建,之后由 should_rebuild_prepare_graph(...) 决定是否重建。
|
||||
*/
|
||||
template <typename T>
|
||||
concept Prepare_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
|
||||
{ private_data.build_prepare_graph(object, state) } -> std::same_as<tf::Taskflow>;
|
||||
concept Prepare_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::Prop& prop) {
|
||||
{ private_data.build_prepare_graph(object, prop) } -> std::same_as<tf::Taskflow>;
|
||||
};
|
||||
/* 最终 Private 声明 No_Prepare 时,该 Renderable 不参与 Prepare 阶段。 */
|
||||
template <typename T>
|
||||
@@ -48,29 +47,29 @@ concept Paint_Data_Renderable = Attached<T> && requires(typename T::Private& pri
|
||||
* 子图首次执行前一定构建,之后由 should_rebuild_paint_graph(...) 决定是否重建。
|
||||
*/
|
||||
template <typename T>
|
||||
concept Paint_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::State& state) {
|
||||
{ private_data.build_paint_graph(object, state) } -> std::same_as<tf::Taskflow>;
|
||||
concept Paint_Graph_Renderable = Attached<T> && requires(typename T::Private& private_data, T* object, const typename T::Prop& prop) {
|
||||
{ private_data.build_paint_graph(object, prop) } -> std::same_as<tf::Taskflow>;
|
||||
};
|
||||
/*
|
||||
* 最终可交给 Scene 执行的 Renderable 契约。
|
||||
* Prepare 与 Paint 各自至少提供数据模式或子图模式之一,并继承 Renderable 提供的默认阶段策略。
|
||||
*/
|
||||
template <typename T>
|
||||
concept Renderable_Object = Attached<T> && std::derived_from<T, Renderable> && requires(typename T::Private& private_data, T* object, const typename T::State& state, bool dirty) {
|
||||
concept Renderable_Object = Attached<T> && std::derived_from<T, Renderable> && requires(typename T::Private& private_data, T* object, const typename T::State& state, const typename T::Prop& prop, bool dirty) {
|
||||
{ private_data.should_prepare(object, state, dirty) } -> std::same_as<bool>;
|
||||
{ private_data.should_paint(object, state, dirty) } -> std::same_as<bool>;
|
||||
{ private_data.should_rebuild_prepare_graph(object, state) } -> std::same_as<bool>;
|
||||
{ private_data.should_rebuild_paint_graph(object, state) } -> std::same_as<bool>;
|
||||
{ private_data.should_rebuild_prepare_graph(object, prop) } -> std::same_as<bool>;
|
||||
{ private_data.should_rebuild_paint_graph(object, prop) } -> std::same_as<bool>;
|
||||
} && (No_Prepare_Renderable<T> || Prepare_Data_Renderable<T> || Prepare_Graph_Renderable<T>) && (Paint_Data_Renderable<T> || Paint_Graph_Renderable<T>);
|
||||
/*
|
||||
* Renderable 定义 Scene 可调度对象的公共机制。
|
||||
* 用户继续派生该定义,在派生类型的 Private 中提供 Prepare/Paint 定制点,并最终使用 Impl<T> 创建可运行实例。
|
||||
*/
|
||||
struct Renderable : Def<Renderable, Root, State_Type<Renderable_State_Tag>> {
|
||||
struct Renderable : Def<Renderable, Root> {
|
||||
/* Renderable 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
|
||||
struct Prop : Prev_Prop {};
|
||||
/* Renderable 每次 Scene 执行后发布的阶段状态与统计。 */
|
||||
struct State : Prev_State<Renderable_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool prepare_dirty{}; /* Prepare 条件判断时观察到的 Prepare_Data_Tag dirty 状态。 */
|
||||
bool paint_dirty{}; /* Paint 条件判断时观察到的 Paint_Tag dirty 状态。 */
|
||||
bool prepare_executed{}; /* 本次 Scene 执行是否运行了 Prepare 数据函数或子图。 */
|
||||
|
||||
@@ -40,9 +40,9 @@ struct Renderable::Private : Prev_Private {
|
||||
bool prepare_graph_built{}; /* Prepare 子图是否至少成功构建过一次。 */
|
||||
bool paint_graph_built{}; /* Paint 子图是否至少成功构建过一次。 */
|
||||
/* CRTP 可覆盖:决定已选中子图模式的 Prepare 子图是否重建;object 为最终对象,state 为当前发布状态;默认返回 false。 */
|
||||
bool should_rebuild_prepare_graph(Attached auto* object, const State& state);
|
||||
bool should_rebuild_prepare_graph(Attached auto* object, const Prop& prop);
|
||||
/* CRTP 可覆盖:决定已选中子图模式的 Paint 子图是否重建;object 为最终对象,state 为当前发布状态;默认返回 false。 */
|
||||
bool should_rebuild_paint_graph(Attached auto* object, const State& state);
|
||||
bool should_rebuild_paint_graph(Attached auto* object, const Prop& prop);
|
||||
/* CRTP 可覆盖:决定本次是否执行 Prepare;object 为最终对象,state 为当前发布状态,dirty 为 Prepare dirty;默认返回 dirty。 */
|
||||
bool should_prepare(Attached auto* object, const State& state, bool dirty);
|
||||
/* CRTP 可覆盖:决定本次是否执行 Paint;object 为最终对象,state 为当前发布状态,dirty 为 Paint dirty;默认返回 dirty。 */
|
||||
@@ -63,10 +63,10 @@ void Renderable::run_prepare_data(Object* object) {
|
||||
};
|
||||
walk_private<true, typename Object::Attached_Object>(object, callback);
|
||||
}
|
||||
inline bool Renderable::Private::should_rebuild_prepare_graph(Attached auto*, const State&) {
|
||||
inline bool Renderable::Private::should_rebuild_prepare_graph(Attached auto*, const Prop&) {
|
||||
return false;
|
||||
}
|
||||
inline bool Renderable::Private::should_rebuild_paint_graph(Attached auto*, const State&) {
|
||||
inline bool Renderable::Private::should_rebuild_paint_graph(Attached auto*, const Prop&) {
|
||||
return false;
|
||||
}
|
||||
inline bool Renderable::Private::should_prepare(Attached auto*, const State&, bool dirty) {
|
||||
@@ -117,7 +117,7 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
return private_data.should_rebuild_prepare_graph(value, *private_data.state.current);
|
||||
return private_data.should_rebuild_prepare_graph(value, *private_data.current);
|
||||
},
|
||||
[]() -> Private::Stage_Run {
|
||||
if constexpr (Prepare_Data_Renderable<Object> && !No_Prepare_Renderable<Object>) {
|
||||
@@ -135,7 +135,7 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
return [](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
return private_data.build_prepare_graph(value, *private_data.state.current);
|
||||
return private_data.build_prepare_graph(value, *private_data.current);
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -152,7 +152,7 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
return private_data.should_rebuild_paint_graph(value, *private_data.state.current);
|
||||
return private_data.should_rebuild_paint_graph(value, *private_data.current);
|
||||
},
|
||||
[]() -> Private::Stage_Run {
|
||||
if constexpr (Paint_Data_Renderable<Object>) {
|
||||
@@ -171,7 +171,7 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
return [](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
auto& private_data = static_cast<typename Object::Private&>(*value->d);
|
||||
return private_data.build_paint_graph(value, *private_data.state.current);
|
||||
return private_data.build_paint_graph(value, *private_data.current);
|
||||
};
|
||||
}
|
||||
else {
|
||||
@@ -187,7 +187,7 @@ inline void Renderable::bind_dependency_graph_object(Attached auto* object) {
|
||||
},
|
||||
[](Root* root) {
|
||||
auto* value = static_cast<Object*>(root);
|
||||
value->template notify_state<Renderable_State_Tag>();
|
||||
value->template notify_state<Renderable::Base_Tag>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2,16 +2,15 @@
|
||||
#include "renderable.hpp"
|
||||
namespace aethera {
|
||||
/* Scene 状态标签,用于访问和订阅 Scene::State。 */
|
||||
struct Scene_State_Tag {};
|
||||
/*
|
||||
* Scene 汇总 Prepare/Paint 两张 Dependency_Graph 图并构建总 Taskflow。
|
||||
* 用户最终通过 Impl<Scene> 创建可使用实例;编辑 Dependency_Graph 后调用 advance() 提交结构变化,再调用 process(...) 执行当前场景。
|
||||
*/
|
||||
struct Scene : Def<Scene, Root, State_Type<Scene_State_Tag>, Dependency_Graph_Type<Prepare_Data_Tag, Renderable>, Dependency_Graph_Type<Paint_Tag, Renderable>> {
|
||||
struct Scene : Def<Scene, Root, Dependency_Graph_Type<Prepare_Data_Tag, Renderable>, Dependency_Graph_Type<Paint_Tag, Renderable>> {
|
||||
/* Scene 当前没有额外发布属性;派生定义可在自己的 Prop 中继续追加字段。 */
|
||||
struct Prop : Prev_Prop {};
|
||||
/* Scene 每次 process(...) 后发布的总图结构与执行统计。 */
|
||||
struct State : Prev_State<Scene_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool taskflow_rebuilt{}; /* 本次 process(...) 前的 advance 是否重新构建了总 Taskflow。 */
|
||||
std::size_t renderable_count{}; /* Prepare/Paint 两张依赖图中去重后的 Renderable 数量。 */
|
||||
std::size_t taskflow_task_count{}; /* 当前总 Taskflow 中的任务节点数量。 */
|
||||
|
||||
@@ -31,7 +31,7 @@ void Scene::Private::process(Object* object, Callback&& callback) requires std::
|
||||
auto& state = static_cast<State&>(*private_data.state.current);
|
||||
state.taskflow_execution_time_ns = 0;
|
||||
if (runtime->taskflow && !runtime->taskflow->empty()) state.taskflow_execution_time_ns = detail::run_taskflow(*runtime->taskflow);
|
||||
object->template notify_state<Scene_State_Tag>();
|
||||
object->template notify_state<Scene::Base_Tag>();
|
||||
Result result;
|
||||
std::invoke(std::forward<Callback>(callback), std::as_const(result));
|
||||
}
|
||||
@@ -42,7 +42,7 @@ void Scene::Private::after_advance(Object* object,
|
||||
const Prop* current_prop,
|
||||
State_Access<State> current_states) {
|
||||
auto* resource = detail::task_memory_resource();
|
||||
auto& scene_state = current_states.get<Scene_State_Tag>();
|
||||
auto& scene_state = current_states.get<Scene::Base_Tag>();
|
||||
scene_state.taskflow_rebuilt = false;
|
||||
std::pmr::unordered_set<Root*> advanced_objects{resource};
|
||||
advanced_objects.insert(object);
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include "double_buffer/model.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
namespace {
|
||||
struct Node_State_Tag {};
|
||||
struct Graph_State_Tag {};
|
||||
struct Graph_Tag {};
|
||||
struct Node_Object : double_buffer::Def<Node_Object, double_buffer::Root, double_buffer::State_Type<Node_State_Tag>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Node_State_Tag> {
|
||||
struct Node_Object : double_buffer::Def<Node_Object, double_buffer::Root> {
|
||||
struct Prop : Prev_Prop { int value{}; int other{}; };
|
||||
struct State : Prev_State {
|
||||
int value{}; /* 字段级依赖测试使用的状态值。 */
|
||||
int other{}; /* 状态层级依赖测试使用的同层其他值。 */
|
||||
bool operator==(const State&) const = default;
|
||||
@@ -14,9 +12,9 @@ struct Node_Object : double_buffer::Def<Node_Object, double_buffer::Root, double
|
||||
struct Private : Prev_Private {};
|
||||
};
|
||||
using Node = double_buffer::Impl<Node_Object>;
|
||||
struct Graph_Object : double_buffer::Def<Graph_Object, double_buffer::Root, double_buffer::State_Type<Graph_State_Tag>, double_buffer::Dependency_Graph_Type<Graph_Tag, Node_Object>> {
|
||||
struct Graph_Object : double_buffer::Def<Graph_Object, double_buffer::Root, double_buffer::Dependency_Graph_Type<Graph_Tag, Node_Object>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Graph_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {};
|
||||
@@ -35,7 +33,8 @@ std::unique_ptr<Object_Type> build_object() {
|
||||
static_assert(!double_buffer::detail::Dependency_Object<Node_Object>);
|
||||
static_assert(double_buffer::detail::Dependency_Object<Node>);
|
||||
static_assert(!double_buffer::detail::State_Dependency_Source<Node_Object, &Node_Object::State::value>);
|
||||
static_assert(double_buffer::detail::State_Layer_Dependency_Source<Node, Node_State_Tag>);
|
||||
static_assert(double_buffer::detail::State_Layer_Dependency_Source<Node, Node_Object::Base_Tag>);
|
||||
static_assert(double_buffer::detail::Prop_Layer_Dependency_Source<Node, Node_Object::Base_Tag>);
|
||||
}
|
||||
TEST(dependency_graph_storage, edited_graph_commits_and_stays_synchronized) {
|
||||
auto first = build_object<Node>();
|
||||
@@ -111,7 +110,7 @@ TEST(dependency_graph, state_dependency_granularity_is_selectable) {
|
||||
editor.add(layer_source.get());
|
||||
editor.add(layer_target.get());
|
||||
editor.template add_dependency<&Node_Object::State::value>(member_target.get(), member_source.get());
|
||||
editor.template add_state_dependency<Node_State_Tag>(layer_target.get(), layer_source.get());
|
||||
editor.template add_state_dependency<Node_Object::Base_Tag>(layer_target.get(), layer_source.get());
|
||||
}
|
||||
).has_value());
|
||||
graph->advance();
|
||||
@@ -122,6 +121,28 @@ TEST(dependency_graph, state_dependency_granularity_is_selectable) {
|
||||
layer_source->update_state<&Node_Object::State::other>(3);
|
||||
EXPECT_TRUE(layer_target->dirty<Graph_Tag>());
|
||||
}
|
||||
TEST(dependency_graph, prop_dependency_granularity_is_selectable) {
|
||||
auto member_source = build_object<Node>();
|
||||
auto member_target = build_object<Node>();
|
||||
auto layer_source = build_object<Node>();
|
||||
auto layer_target = build_object<Node>();
|
||||
auto graph = build_object<Graph>();
|
||||
ASSERT_TRUE(graph->edit_dependency_graph<Graph_Tag>([&](auto& editor) {
|
||||
editor.add(member_source.get());
|
||||
editor.add(member_target.get());
|
||||
editor.add(layer_source.get());
|
||||
editor.add(layer_target.get());
|
||||
editor.template add_prop_dependency<&Node_Object::Prop::value>(member_target.get(), member_source.get());
|
||||
editor.template add_prop_dependency<Node_Object::Base_Tag>(layer_target.get(), layer_source.get());
|
||||
}).has_value());
|
||||
graph->advance();
|
||||
member_source->set<&Node_Object::Prop::other>(1);
|
||||
EXPECT_FALSE(member_target->dirty<Graph_Tag>());
|
||||
member_source->set<&Node_Object::Prop::value>(2);
|
||||
EXPECT_TRUE(member_target->dirty<Graph_Tag>());
|
||||
layer_source->set<&Node_Object::Prop::other>(3);
|
||||
EXPECT_TRUE(layer_target->dirty<Graph_Tag>());
|
||||
}
|
||||
TEST(dependency_graph, cycle_is_rejected_before_pending_graph_commit) {
|
||||
auto first = build_object<Node>();
|
||||
auto second = build_object<Node>();
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#include "double_buffer/model.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
namespace {
|
||||
struct Object_State_Tag {};
|
||||
struct Object_Buffer_Tag {};
|
||||
struct Test_Object : double_buffer::Def<Test_Object, double_buffer::Root, double_buffer::State_Type<Object_State_Tag>, double_buffer::Tagged_Buffer<Object_Buffer_Tag, int>> {
|
||||
struct Test_Object : double_buffer::Def<Test_Object, double_buffer::Root, double_buffer::Tagged_Buffer<Object_Buffer_Tag, int>> {
|
||||
struct Prop : Prev_Prop {
|
||||
int first{};
|
||||
int second{};
|
||||
};
|
||||
struct State : Prev_State<Object_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
int first{};
|
||||
int second{};
|
||||
bool operator==(const State&) const = default;
|
||||
@@ -20,20 +19,18 @@ using Object = double_buffer::Impl<Test_Object>;
|
||||
inline auto& Test_Object::data_for_test() {
|
||||
return static_cast<Object::Private&>(*d);
|
||||
}
|
||||
struct Derived_State_Tag {};
|
||||
struct Derived_Object : double_buffer::Def<Derived_Object, Test_Object, double_buffer::State_Type<Derived_State_Tag>> {
|
||||
struct Derived_Object : double_buffer::Def<Derived_Object, Test_Object> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Derived_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
int derived{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {};
|
||||
};
|
||||
using Derived = double_buffer::Impl<Derived_Object>;
|
||||
struct Lifetime_State_Tag {};
|
||||
struct Lifetime_Object : double_buffer::Def<Lifetime_Object, double_buffer::Root, double_buffer::State_Type<Lifetime_State_Tag>> {
|
||||
struct Lifetime_Object : double_buffer::Def<Lifetime_Object, double_buffer::Root> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Lifetime_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
@@ -84,13 +81,13 @@ TEST(object_buffer, state_commits_and_keeps_incremental_baseline) {
|
||||
}
|
||||
TEST(object_buffer, prop_publishes_and_keeps_incremental_baseline) {
|
||||
auto object = build_object<Object>();
|
||||
object->set(&Test_Object::Prop::first, 17);
|
||||
object->set<&Test_Object::Prop::first>(17);
|
||||
EXPECT_EQ(object->data_for_test().current->first, 17);
|
||||
EXPECT_EQ(object->data_for_test().pending->first, 0);
|
||||
object->advance();
|
||||
EXPECT_EQ(object->data_for_test().pending->first, 17);
|
||||
EXPECT_EQ(object->data_for_test().current->first, 17);
|
||||
object->set(&Test_Object::Prop::second, 19);
|
||||
object->set<&Test_Object::Prop::second>(19);
|
||||
object->advance();
|
||||
EXPECT_EQ(object->data_for_test().pending->first, 17);
|
||||
EXPECT_EQ(object->data_for_test().pending->second, 19);
|
||||
@@ -98,7 +95,7 @@ TEST(object_buffer, prop_publishes_and_keeps_incremental_baseline) {
|
||||
TEST(state_tag, callback_publishes_only_requested_layer) {
|
||||
auto object = build_object<Object>();
|
||||
int calls = 0;
|
||||
object->set_state_callback<Object_State_Tag>(
|
||||
object->set_state_callback<Test_Object::Base_Tag>(
|
||||
[&](const auto& state) {
|
||||
++calls;
|
||||
EXPECT_EQ(state.first, 23);
|
||||
@@ -107,14 +104,14 @@ TEST(state_tag, callback_publishes_only_requested_layer) {
|
||||
object->update_state<&Test_Object::State::first>(23);
|
||||
object->advance();
|
||||
EXPECT_EQ(calls, 0);
|
||||
object->notify_state<Object_State_Tag>();
|
||||
object->notify_state<Test_Object::Base_Tag>();
|
||||
EXPECT_EQ(calls, 1);
|
||||
}
|
||||
static_assert(requires(Object& object) {
|
||||
object.template access_state<Object_State_Tag>([](const auto&) {});
|
||||
object.template access_state<Test_Object::Base_Tag>([](const auto&) {});
|
||||
});
|
||||
static_assert(std::same_as<
|
||||
decltype(std::declval<const Object&>().template read_state<Object_State_Tag>()),
|
||||
decltype(std::declval<const Object&>().template read_state<Test_Object::Base_Tag>()),
|
||||
const Test_Object::State&
|
||||
>);
|
||||
struct Missing_State_Tag {};
|
||||
@@ -123,12 +120,12 @@ TEST(state_tag, inherited_tags_remain_independently_addressable) {
|
||||
auto object = build_object<Derived>();
|
||||
int base_calls = 0;
|
||||
int derived_calls = 0;
|
||||
object->set_state_callback<Object_State_Tag>([&](const auto&) { ++base_calls; });
|
||||
object->set_state_callback<Derived_State_Tag>([&](const auto&) { ++derived_calls; });
|
||||
object->notify_state<Object_State_Tag>();
|
||||
object->set_state_callback<Test_Object::Base_Tag>([&](const auto&) { ++base_calls; });
|
||||
object->set_state_callback<Derived_Object::Base_Tag>([&](const auto&) { ++derived_calls; });
|
||||
object->notify_state<Test_Object::Base_Tag>();
|
||||
EXPECT_EQ(base_calls, 1);
|
||||
EXPECT_EQ(derived_calls, 0);
|
||||
object->notify_state<Derived_State_Tag>();
|
||||
object->notify_state<Derived_Object::Base_Tag>();
|
||||
EXPECT_EQ(base_calls, 1);
|
||||
EXPECT_EQ(derived_calls, 1);
|
||||
}
|
||||
@@ -137,31 +134,31 @@ TEST(state_tag, committed_layer_is_directly_readable_by_tag) {
|
||||
object->update_state<&Test_Object::State::first>(41);
|
||||
object->update_state<&Derived_Object::State::derived>(43);
|
||||
object->advance();
|
||||
EXPECT_EQ(object->read_state<Object_State_Tag>().first, 41);
|
||||
EXPECT_EQ(object->read_state<Derived_State_Tag>().derived, 43);
|
||||
EXPECT_EQ(object->read_state<Test_Object::Base_Tag>().first, 41);
|
||||
EXPECT_EQ(object->read_state<Derived_Object::Base_Tag>().derived, 43);
|
||||
}
|
||||
static_assert(double_buffer::detail::State_Tag_In<double_buffer::Root_State_Tag, Derived::States>);
|
||||
static_assert(double_buffer::detail::State_Tag_In<Object_State_Tag, Derived::States>);
|
||||
static_assert(double_buffer::detail::State_Tag_In<Derived_State_Tag, Derived::States>);
|
||||
static_assert(double_buffer::detail::State_Tag_In<double_buffer::Root::Base_Tag, Derived::States>);
|
||||
static_assert(double_buffer::detail::State_Tag_In<Test_Object::Base_Tag, Derived::States>);
|
||||
static_assert(double_buffer::detail::State_Tag_In<Derived_Object::Base_Tag, Derived::States>);
|
||||
static_assert(std::same_as<
|
||||
decltype(std::declval<double_buffer::State_Access<Derived::State>>().template get<Object_State_Tag>()),
|
||||
decltype(std::declval<double_buffer::State_Access<Derived::State>>().template get<Test_Object::Base_Tag>()),
|
||||
Test_Object::State&
|
||||
>);
|
||||
static_assert(std::same_as<
|
||||
decltype(std::declval<double_buffer::State_Access<const Derived::State>>().template get<Derived_State_Tag>()),
|
||||
decltype(std::declval<double_buffer::State_Access<const Derived::State>>().template get<Derived_Object::Base_Tag>()),
|
||||
const Derived_Object::State&
|
||||
>);
|
||||
TEST(state_tag, state_access_selects_mutable_and_const_layers_by_tag) {
|
||||
Derived::State state;
|
||||
double_buffer::State_Access states{state};
|
||||
states.get<Object_State_Tag>().first = 31;
|
||||
states.get<Derived_State_Tag>().derived = 47;
|
||||
states.get<Test_Object::Base_Tag>().first = 31;
|
||||
states.get<Derived_Object::Base_Tag>().derived = 47;
|
||||
double_buffer::State_Access<Test_Object::State> base_states = states;
|
||||
EXPECT_EQ(base_states.get<Object_State_Tag>().first, 31);
|
||||
EXPECT_EQ(base_states.get<Test_Object::Base_Tag>().first, 31);
|
||||
const auto& const_state = state;
|
||||
double_buffer::State_Access current_states{const_state};
|
||||
EXPECT_EQ(current_states.get<Object_State_Tag>().first, 31);
|
||||
EXPECT_EQ(current_states.get<Derived_State_Tag>().derived, 47);
|
||||
EXPECT_EQ(current_states.get<Test_Object::Base_Tag>().first, 31);
|
||||
EXPECT_EQ(current_states.get<Derived_Object::Base_Tag>().derived, 47);
|
||||
}
|
||||
TEST(state_tag, state_chain_keeps_default_equality_usable) {
|
||||
Test_Object::State first;
|
||||
@@ -170,3 +167,21 @@ TEST(state_tag, state_chain_keeps_default_equality_usable) {
|
||||
second.first = 1;
|
||||
EXPECT_FALSE(first == second);
|
||||
}
|
||||
static_assert(std::same_as<
|
||||
decltype(std::declval<double_buffer::Prop_Access<Derived::Prop>>().template get<Test_Object::Base_Tag>()),
|
||||
Test_Object::Prop&
|
||||
>);
|
||||
static_assert(std::same_as<
|
||||
decltype(std::declval<double_buffer::Private_Access<Derived_Object::Private>>().template get<Test_Object::Base_Tag>()),
|
||||
Test_Object::Private&
|
||||
>);
|
||||
TEST(base_tag, prop_state_and_private_domains_are_independently_addressable) {
|
||||
Derived::Prop prop;
|
||||
double_buffer::Prop_Access props{prop};
|
||||
props.get<Test_Object::Base_Tag>().first = 53;
|
||||
EXPECT_EQ(props.get<Test_Object::Base_Tag>().first, 53);
|
||||
Derived_Object::Private private_data;
|
||||
double_buffer::Private_Access private_layers{private_data};
|
||||
EXPECT_EQ(&private_layers.get<Test_Object::Base_Tag>(), static_cast<Test_Object::Private*>(&private_data));
|
||||
EXPECT_EQ(&private_layers.get<Derived_Object::Base_Tag>(), static_cast<Derived_Object::Private*>(&private_data));
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#include "scene.hpp"
|
||||
#include <gtest/gtest.h>
|
||||
namespace {
|
||||
struct Direct_State_Tag {};
|
||||
struct Graph_State_Tag {};
|
||||
struct Direct_Renderable : double_buffer::Def<Direct_Renderable, aethera::Renderable, double_buffer::State_Type<Direct_State_Tag>> {
|
||||
struct Direct_Renderable : double_buffer::Def<Direct_Renderable, aethera::Renderable> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Direct_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
@@ -20,9 +18,9 @@ struct Direct_Renderable : double_buffer::Def<Direct_Renderable, aethera::Render
|
||||
};
|
||||
Private& data_for_test();
|
||||
};
|
||||
struct Graph_Renderable : double_buffer::Def<Graph_Renderable, aethera::Renderable, double_buffer::State_Type<Graph_State_Tag>> {
|
||||
struct Graph_Renderable : double_buffer::Def<Graph_Renderable, aethera::Renderable> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Graph_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {
|
||||
@@ -30,13 +28,13 @@ struct Graph_Renderable : double_buffer::Def<Graph_Renderable, aethera::Renderab
|
||||
int paint_calls{};
|
||||
int prepare_builds{};
|
||||
bool rebuild_prepare{};
|
||||
tf::Taskflow build_prepare_graph(double_buffer::Attached auto*, const State&) {
|
||||
tf::Taskflow build_prepare_graph(double_buffer::Attached auto*, const Prop&) {
|
||||
++prepare_builds;
|
||||
tf::Taskflow graph;
|
||||
graph.emplace([this] { ++prepare_calls; }).name("test.prepare.graph.task");
|
||||
return graph;
|
||||
}
|
||||
bool should_rebuild_prepare_graph(double_buffer::Attached auto*, const State&) {
|
||||
bool should_rebuild_prepare_graph(double_buffer::Attached auto*, const Prop&) {
|
||||
return std::exchange(rebuild_prepare, false);
|
||||
}
|
||||
void paint(double_buffer::Attached auto*) {
|
||||
@@ -54,10 +52,9 @@ inline Direct_Renderable::Private& Direct_Renderable::data_for_test() {
|
||||
inline Graph_Renderable::Private& Graph_Renderable::data_for_test() {
|
||||
return static_cast<Private&>(*d);
|
||||
}
|
||||
struct Dependency_State_Tag {};
|
||||
struct Dependency_Renderable : double_buffer::Def<Dependency_Renderable, aethera::Renderable, double_buffer::State_Type<Dependency_State_Tag>> {
|
||||
struct Dependency_Renderable : double_buffer::Def<Dependency_Renderable, aethera::Renderable> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State<Dependency_State_Tag> {
|
||||
struct State : Prev_State {
|
||||
int revision{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
@@ -141,12 +138,12 @@ TEST(renderable_state, scene_and_renderable_callbacks_publish_at_stage_boundarie
|
||||
int renderable_updates = 0;
|
||||
int scene_updates = 0;
|
||||
int runtime_updates = 0;
|
||||
renderable->set_state_callback<aethera::Renderable_State_Tag>([&](const auto& state) {
|
||||
renderable->set_state_callback<aethera::Renderable::Base_Tag>([&](const auto& state) {
|
||||
++renderable_updates;
|
||||
EXPECT_TRUE(state.prepare_executed);
|
||||
EXPECT_TRUE(state.paint_executed);
|
||||
});
|
||||
scene->set_state_callback<aethera::Scene_State_Tag>([&](const auto& state) {
|
||||
scene->set_state_callback<aethera::Scene::Base_Tag>([&](const auto& state) {
|
||||
++scene_updates;
|
||||
EXPECT_GT(state.taskflow_task_count, 0u);
|
||||
});
|
||||
@@ -169,7 +166,7 @@ TEST(scene_state, structural_statistics_survive_a_process_without_rebuild) {
|
||||
std::size_t task_count{};
|
||||
std::size_t dependency_count{};
|
||||
int updates{};
|
||||
scene->set_state_callback<aethera::Scene_State_Tag>([&](const auto& state) {
|
||||
scene->set_state_callback<aethera::Scene::Base_Tag>([&](const auto& state) {
|
||||
EXPECT_EQ(state.renderable_count, 1u);
|
||||
EXPECT_GT(state.taskflow_task_count, 0u);
|
||||
if (updates == 0) {
|
||||
|
||||
Reference in New Issue
Block a user