Skip to content

Commit

Permalink
Implement duplex_apply.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Oct 10, 2020
1 parent 62f1905 commit 962005c
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 3 deletions.
84 changes: 81 additions & 3 deletions src/alia/signals/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ lazy_lift(Function f)
// result of applying the function :f to the values of :args. Unlike
// lazy_apply, this is eager and caches and the result.

namespace detail {

enum class apply_status
{
UNCOMPUTED,
Expand Down Expand Up @@ -204,8 +206,8 @@ process_apply_args(
}

template<class Function, class... Args>
auto
apply(context ctx, Function f, Args const&... args)
auto&
process_apply(context ctx, Function f, Args const&... args)
{
apply_result_data<decltype(f(read_signal(args)...))>* data_ptr;
get_cached_data(ctx, &data_ptr);
Expand All @@ -230,7 +232,16 @@ apply(context ctx, Function f, Args const&... args)
if (data.status == apply_status::FAILED)
std::rethrow_exception(data.error);
}
return make_apply_signal(data);
return data;
}

} // namespace detail

template<class Function, class... Args>
auto
apply(context ctx, Function f, Args const&... args)
{
return detail::make_apply_signal(detail::process_apply(ctx, f, args...));
}

template<class Function>
Expand All @@ -242,6 +253,73 @@ lift(Function f)
};
}

namespace detail {

template<class Value, class Wrapped, class Reverse>
struct duplex_apply_signal : signal<
duplex_apply_signal<Value, Wrapped, Reverse>,
Value,
duplex_signal>
{
duplex_apply_signal(
apply_result_data<Value>& data, Wrapped wrapped, Reverse reverse)
: data_(&data),
wrapped_(std::move(wrapped)),
reverse_(std::move(reverse))
{
}
id_interface const&
value_id() const
{
id_ = make_id(data_->version);
return id_;
}
bool
has_value() const
{
return data_->status == apply_status::READY;
}
Value const&
read() const
{
return data_->result;
}
bool
ready_to_write() const
{
return wrapped_.ready_to_write();
}
void
write(typename Value value) const
{
wrapped_.write(reverse_(std::move(value)));
}

private:
apply_result_data<Value>* data_;
mutable simple_id<counter_type> id_;
Wrapped wrapped_;
Reverse reverse_;
};
template<class Value, class Wrapped, class Reverse>
auto
make_duplex_apply_signal(
apply_result_data<Value>& data, Wrapped wrapped, Reverse reverse)
{
return duplex_apply_signal<Value, Wrapped, Reverse>(
data, std::move(wrapped), std::move(reverse));
}

} // namespace detail

template<class Forward, class Reverse, class Arg>
auto
duplex_apply(context ctx, Forward forward, Reverse reverse, Arg arg)
{
return detail::make_duplex_apply_signal(
detail::process_apply(ctx, forward, arg), arg, reverse);
}

// alia_mem_fn(m) wraps a member function name in a lambda so that it can be
// passed as a function object. (It's the equivalent of std::mem_fn, but
// there's no need to provide the type name.)
Expand Down
54 changes: 54 additions & 0 deletions unit_tests/signals/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,3 +287,57 @@ TEST_CASE("alia_mem_fn", "[signals][application]")
REQUIRE(
read_signal(lazy_apply(alia_mem_fn(substr), v, value(5))) == "text");
}

TEST_CASE("duplex apply", "[signals][application]")
{
alia::system sys;
initialize_system(sys, [](context) {});

int n = 0;

captured_id signal_id;

auto make_controller = [&](double new_value) {
return [=, &n, &signal_id](context ctx) {
auto f = [](int x) -> double { return x * 2.0; };
auto r = [](double x) -> int { return int(x / 2.0 + 0.5); };

auto s = duplex_apply(ctx, f, r, direct(n));

typedef decltype(s) signal_t;
REQUIRE(signal_is_readable<signal_t>::value);
REQUIRE(signal_is_writable<signal_t>::value);

REQUIRE(signal_has_value(s));
REQUIRE(read_signal(s) == n * 2.0);

signal_id.capture(s.value_id());

REQUIRE(s.ready_to_write());
if (new_value > 0)
write_signal(s, new_value);
};
};

do_traversal(sys, make_controller(0));
REQUIRE(n == 0);

{
captured_id last_id = signal_id;
do_traversal(sys, make_controller(4));
REQUIRE(n == 2);

do_traversal(sys, make_controller(4));
REQUIRE(n == 2);
REQUIRE(last_id != signal_id);
}

do_traversal(sys, make_controller(0));
REQUIRE(n == 2);

do_traversal(sys, make_controller(2));
REQUIRE(n == 1);

do_traversal(sys, make_controller(0));
REQUIRE(n == 1);
}

0 comments on commit 962005c

Please sign in to comment.