中间状态暂存

This commit is contained in:
2026-08-14 17:53:13 +08:00
parent 260f649b08
commit bb64801679
75 changed files with 2984 additions and 1823 deletions
@@ -2,9 +2,9 @@
#include "State_Test_Types.hpp"
static_assert(State_Value<State_Plain_Value>);
static_assert(std::derived_from<State_Plain_Strategy, State_Strategy_Base>);
static_assert(Double_State_Strategy_Type<State_Plain_Strategy>);
static_assert(Double_State_Storage_Type<State_Plain_Strategy>);
static_assert(!Has_State_Validator<State_Plain_Strategy, State_Plain_Value>);
static_assert(Has_State_Validator<State_Checked_Strategy, State_Checked_Value>);
static_assert(Has_State_Validator<State_Checked_Validator, State_Checked_Value>);
TEST(state_concepts_test, concepts_compile) {
SUCCEED();
}
@@ -2,23 +2,25 @@
#include <cstdint>
#include <memory>
#include "State_Test_Types.hpp"
TEST(double_state_strategy_test, builds_without_state_validator) {
auto product = State_Plain_Product::Builder{}.set<&State_Plain_Value::count>(10).set<&State_Plain_Value::name>("alpha").build();
EXPECT_EQ(product->get<&State_Plain_Value::count>(), 10);
EXPECT_EQ(product->get<&State_Plain_Value::name>(), "alpha");
TEST(double_state_strategy_test, constructs_from_state) {
State_Plain_Strategy state({.count = 10, .name = "alpha"});
EXPECT_EQ(state.get<&State_Plain_Value::count>(), 10);
EXPECT_EQ(state.get<&State_Plain_Value::name>(), "alpha");
}
TEST(double_state_strategy_test, calls_optional_state_validator_when_present) {
auto builder = State_Checked_Product::Builder{}.set<&State_Checked_Value::minimum>(10).set<&State_Checked_Value::maximum>(5);
EXPECT_THROW(builder.build(1, "invalid"), std::invalid_argument);
State_Checked_Value state{.minimum = 10, .maximum = 5};
EXPECT_THROW(State_Checked_Validator::validate_state(state),
std::invalid_argument);
}
TEST(double_state_strategy_test, keeps_cache_separate_until_publish) {
State_Render_State_Reader render;
auto product = State_Checked_Product::Builder{}.set<&State_Checked_Value::percent>(20).set<&State_Checked_Value::batch_size>(4).build(1, "state");
product->set<&State_Checked_Value::percent>(60);
EXPECT_EQ(product->get<&State_Checked_Value::percent>(), 60);
EXPECT_EQ(render.read(*product).percent.get(), 20);
product->publish();
EXPECT_EQ(render.read(*product).percent.get(), 60);
State_Checked_Strategy state(
State_Checked_Value{.percent = 20, .batch_size = 4});
state.set<&State_Checked_Value::percent>(60);
EXPECT_EQ(state.get<&State_Checked_Value::percent>(), 60);
EXPECT_EQ(render.read(state).percent.get(), 20);
state.publish();
EXPECT_EQ(render.read(state).percent.get(), 60);
}
TEST(double_state_strategy_test, keeps_acquired_render_reference_stable_until_publish) {
State_Render_State_Reader render;
@@ -30,22 +32,18 @@ TEST(double_state_strategy_test, keeps_acquired_render_reference_stable_until_pu
EXPECT_EQ(render.read(strategy).count, 2);
}
TEST(double_state_strategy_test, keeps_cached_value_when_runtime_validation_fails) {
auto product = State_Checked_Product::Builder{}.set<&State_Checked_Value::percent>(20).set<&State_Checked_Value::batch_size>(4).build(1, "state");
EXPECT_THROW((product->set<&State_Checked_Value::percent>(200)), std::out_of_range);
EXPECT_EQ(product->get<&State_Checked_Value::percent>(), 20);
State_Checked_Strategy state(
State_Checked_Value{.percent = 20, .batch_size = 4});
EXPECT_THROW((state.set<&State_Checked_Value::percent>(200)),
std::out_of_range);
EXPECT_EQ(state.get<&State_Checked_Value::percent>(), 20);
}
TEST(double_state_strategy_test, supports_atomic_wait_mutex) {
State_Render_State_Reader render;
auto product = State_Wait_Product::Builder{State_Plain_Value{.count = 1, .name = "wait"}}.build();
product->set<&State_Plain_Value::count>(2);
product->publish();
EXPECT_EQ(render.read(*product).count, 2);
}
TEST(double_state_strategy_test, passes_additional_builder_template_arguments) {
static_assert(std::same_as<typename State_Tagged_Product::Builder::Tag, State_Builder_Tag>);
static_assert(std::same_as<typename State_Tagged_Product::Builder::Mode, State_Builder_Mode>);
auto product = State_Tagged_Product::Builder{State_Plain_Value{.count = 3, .name = "tagged"}}.build();
EXPECT_EQ(product->get<&State_Plain_Value::count>(), 3);
State_Wait_Strategy state({.count = 1, .name = "wait"});
state.set<&State_Plain_Value::count>(2);
state.publish();
EXPECT_EQ(render.read(state).count, 2);
}
struct Double_State_Observer_Test_Time_Source {
std::uint64_t value{100};
@@ -70,7 +68,8 @@ struct Double_State_Observer_Test_Recorder {
};
TEST(double_state_strategy_test, notifies_observer_for_cache_update_and_publish) {
using Observer = Observer_State<Double_State_Observer_Test_Recorder, Double_State_Observer_Test_Time_Source>;
using Strategy = Double_State_Strategy<State_Plain_Base, State_Plain_Value, Atomic_Spin_Mutex, Observer>;
using Strategy =
Double_State_Storage<State_Plain_Value, Atomic_Spin_Mutex, Observer>;
Double_State_Observer_Test_Recorder recorder;
Strategy strategy(State_Plain_Value{}, With_Observer(Observer(recorder, Double_State_Observer_Test_Time_Source{})));
strategy.set<&State_Plain_Value::count>(8);
@@ -99,7 +98,7 @@ struct Double_State_Throwing_Assignment_State {
};
TEST(double_state_strategy_test, failed_publish_keeps_render_state_and_revision_unchanged) {
State_Render_State_Reader render;
using Strategy = Double_State_Strategy<State_Plain_Base, Double_State_Throwing_Assignment_State>;
using Strategy = Double_State_Storage<Double_State_Throwing_Assignment_State>;
Double_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
Strategy strategy(Double_State_Throwing_Assignment_State(1, 2));
strategy.set<&Double_State_Throwing_Assignment_State::first>(10);
@@ -27,9 +27,7 @@ struct State_Plain_Value {
int count{};
std::string name;
};
struct State_Plain_Base {};
using State_Plain_Strategy = Double_State_Strategy<State_Plain_Base, State_Plain_Value>;
using State_Plain_Product = Attach_State_Builder<State_Plain_Strategy, State_Plain_Value>;
using State_Plain_Strategy = Double_State_Storage<State_Plain_Value>;
struct State_Checked_Value {
struct Even_Validator {
void operator()(const int& value) const {
@@ -43,27 +41,13 @@ struct State_Checked_Value {
int minimum{};
int maximum{};
};
struct State_Checked_Base {
State_Checked_Base(int id, std::string name) : id(id), name(std::move(name)) {}
struct State_Checked_Validator {
static void validate_state(const State_Checked_Value& state) {
if (state.minimum > state.maximum) {
throw std::invalid_argument("minimum must not exceed maximum");
}
}
int id;
std::string name;
};
using State_Checked_Strategy = Double_State_Strategy<State_Checked_Base, State_Checked_Value>;
using State_Checked_Product = Attach_State_Builder<State_Checked_Strategy, State_Checked_Value>;
using State_Wait_Product = Attach_State_Builder<Double_State_Strategy<State_Plain_Base, State_Plain_Value, Atomic_Wait_Mutex>, State_Plain_Value>;
struct State_Builder_Tag {};
struct State_Builder_Mode {};
template <Property_Product Product, Property_Set Properties, Property_Validator<Properties> Validator, class Tag_Type, class Mode_Type>
class State_Tagged_Builder : public Property_Builder<Product, Properties, Validator> {
public:
using Base = Property_Builder<Product, Properties, Validator>;
using Tag = Tag_Type;
using Mode = Mode_Type;
using Base::Base;
};
using State_Tagged_Product = Attach_State_Builder<State_Plain_Strategy, State_Plain_Value, State_Tagged_Builder, State_Builder_Tag, State_Builder_Mode>;
using State_Checked_Strategy = Double_State_Storage<State_Checked_Value>;
using State_Wait_Strategy =
Double_State_Storage<State_Plain_Value, Atomic_Wait_Mutex>;
@@ -5,13 +5,12 @@
#include <thread>
#include "renderive/state/State_Strategy.hpp"
#include "State_Test_Types.hpp"
struct Triple_State_Test_Base {};
struct Triple_State_Test_State {
int value{};
};
TEST(triple_state_strategy_test, publishes_then_acquires_render_state) {
State_Render_State_Reader render;
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> strategy;
Triple_State_Storage<Triple_State_Test_State> strategy;
strategy.set<&Triple_State_Test_State::value>(7);
strategy.publish();
EXPECT_EQ(render.read(strategy).value, 0);
@@ -20,7 +19,7 @@ TEST(triple_state_strategy_test, publishes_then_acquires_render_state) {
}
TEST(triple_state_strategy_test, returns_render_state_snapshot) {
State_Render_State_Reader render;
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> strategy;
Triple_State_Storage<Triple_State_Test_State> strategy;
const auto snapshot = render.read(strategy);
strategy.set<&Triple_State_Test_State::value>(7);
strategy.publish();
@@ -28,16 +27,14 @@ TEST(triple_state_strategy_test, returns_render_state_snapshot) {
EXPECT_EQ(snapshot.value, 0);
EXPECT_EQ(render.read(strategy).value, 7);
}
struct Multi_State_A_Base {};
struct Multi_State_B_Base {};
struct Multi_State_A {
int value{};
};
struct Multi_State_B {
int value{};
};
struct Multi_State_A_Strategy : Double_State_Strategy<Multi_State_A_Base, Multi_State_A> {};
struct Multi_State_B_Strategy : Double_State_Strategy<Multi_State_B_Base, Multi_State_B> {};
struct Multi_State_A_Strategy : Double_State_Storage<Multi_State_A> {};
struct Multi_State_B_Strategy : Double_State_Storage<Multi_State_B> {};
struct Multi_State_Product : Multi_State_A_Strategy, Multi_State_B_Strategy {};
TEST(double_state_strategy_test, supports_named_base_access_in_multiple_inheritance) {
State_Render_State_Reader render;
@@ -51,7 +48,7 @@ TEST(double_state_strategy_test, supports_named_base_access_in_multiple_inherita
}
TEST(triple_state_strategy_test, concurrently_acquires_render_revision_without_data_race) {
State_Render_State_Reader render;
Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Test_State> strategy;
Triple_State_Storage<Triple_State_Test_State> strategy;
std::atomic<bool> finished{};
auto acquire = [&] {
while (!finished.load(std::memory_order_acquire)) {
@@ -91,7 +88,7 @@ struct Triple_State_Throwing_Assignment_State {
};
TEST(triple_state_strategy_test, failed_publish_keeps_published_revision_and_render_state_unchanged) {
State_Render_State_Reader render;
using Strategy = Triple_State_Strategy<Triple_State_Test_Base, Triple_State_Throwing_Assignment_State>;
using Strategy = Triple_State_Storage<Triple_State_Throwing_Assignment_State>;
Triple_State_Throwing_Assignment_State::throw_on_copy_assignment = false;
Strategy strategy(Triple_State_Throwing_Assignment_State(1, 2));
strategy.set<&Triple_State_Throwing_Assignment_State::first>(10);