Skip to content

Commit

Permalink
Add transient state.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Dec 10, 2020
1 parent 2fdee45 commit 7fb600c
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 6 deletions.
49 changes: 43 additions & 6 deletions src/alia/signals/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,25 @@ make_state_signal(state_storage<Value>& data)
return state_signal<Value, copyable_duplex_signal>(&data);
}

namespace detail {

template<class Context, class Value, class InitialValueSignal>
void
common_state_signal_logic(
Context ctx,
state_storage<Value>* state,
InitialValueSignal const& initial_value_signal)
{
on_refresh(ctx, [&](auto ctx) {
state->refresh_container(get_active_component_container(ctx));
if (!state->is_initialized() && signal_has_value(initial_value_signal))
state->untracked_nonconst_ref()
= read_signal(initial_value_signal);
});
}

} // namespace detail

// get_state(ctx, initial_value) returns a signal carrying some persistent
// local state whose initial value is determined by the :initial_value signal.
// The returned signal will not have a value until :initial_value has one or
Expand All @@ -207,12 +226,30 @@ get_state(Context ctx, InitialValue const& initial_value)
state_storage<typename decltype(initial_value_signal)::value_type>* state;
get_data(ctx, &state);

on_refresh(ctx, [&](auto ctx) {
state->refresh_container(get_active_component_container(ctx));
if (!state->is_initialized() && signal_has_value(initial_value_signal))
state->untracked_nonconst_ref()
= read_signal(initial_value_signal);
});
detail::common_state_signal_logic(ctx, state, initial_value_signal);

return make_state_signal(*state);
}

// get_transient_state(ctx, initial_value) returns a signal carrying some
// transient local state whose initial value is determined by the
// :initial_value signal. The returned signal will not have a value until
// :initial_value has one or one is explicitly written to the state signal.
//
// Unlike get_state, this returns state that does *not* persist when the
// component is inactive. Rather, it is reinitialized whenever the component is
// activated.
//
template<class Context, class InitialValue>
auto
get_transient_state(Context ctx, InitialValue const& initial_value)
{
auto initial_value_signal = signalize(initial_value);

state_storage<typename decltype(initial_value_signal)::value_type>* state;
get_cached_data(ctx, &state);

detail::common_state_signal_logic(ctx, state, initial_value_signal);

return make_state_signal(*state);
}
Expand Down
71 changes: 71 additions & 0 deletions unit_tests/signals/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,74 @@ TEST_CASE("get_state benchmarks", "[signals][state]")
};
}
#endif

TEST_CASE("get_transient_state", "[signals][state]")
{
// The first few passes are all the same as the normal state tests...
alia::system sys;
initialize_system(sys, [](context) {});
do_traversal(sys, [&](context ctx) {
ALIA_IF(true)
{
auto state = get_transient_state(ctx, empty<int>());

REQUIRE(!signal_has_value(state));
REQUIRE(signal_ready_to_write(state));
}
ALIA_END
});
captured_id state_id;
do_traversal(sys, [&](context ctx) {
ALIA_IF(true)
{
auto state = get_transient_state(ctx, value(12));

REQUIRE(signal_has_value(state));
REQUIRE(read_signal(state) == 12);
REQUIRE(signal_ready_to_write(state));
state_id.capture(state.value_id());
}
ALIA_END
});
do_traversal(sys, [&](context ctx) {
ALIA_IF(true)
{
auto state = get_transient_state(ctx, value(12));

if (read_signal(state) != 13)
write_signal(state, 13);
}
ALIA_END
});
do_traversal(sys, [&](context ctx) {
ALIA_IF(true)
{
auto state = get_transient_state(ctx, value(12));

REQUIRE(read_signal(state) == 13);
REQUIRE(!state_id.matches(state.value_id()));
state_id.capture(state.value_id());
}
ALIA_END
});
// Now test that if the state goes inactivate for a pass, it's reset.
do_traversal(sys, [&](context ctx) {
ALIA_IF(false)
{
auto state = get_transient_state(ctx, value(12));
}
ALIA_END
});
do_traversal(sys, [&](context ctx) {
ALIA_IF(true)
{
auto state = get_transient_state(ctx, value(12));

REQUIRE(signal_has_value(state));
REQUIRE(read_signal(state) == 12);
REQUIRE(!state_id.matches(state.value_id()));
REQUIRE(signal_ready_to_write(state));
}
ALIA_END
});
}

0 comments on commit 7fb600c

Please sign in to comment.