Skip to content

Commit

Permalink
Add ALIA_DEFINE_STRUCT_SIGNALS
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Dec 30, 2020
1 parent 23d9635 commit 74b860c
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/alia/signals/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ struct signal<

// signal_ref is a reference to a signal that acts as a signal itself.
template<class Value, class Capabilities>
struct signal_ref final
struct signal_ref
: signal<signal_ref<Value, Capabilities>, Value, Capabilities>
{
// Construct from any signal with compatible capabilities.
Expand Down
78 changes: 78 additions & 0 deletions src/alia/signals/field_macros.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#ifndef ALIA_SIGNAL_FIELD_MACROS_HPP
#define ALIA_SIGNAL_FIELD_MACROS_HPP

#include <alia/signals/operators.hpp>

// __declspec(property(...)) is non-standard.
#if defined(__clang__) || defined(_MSC_VER)

#define ALIA_PP_CONCAT(a, b) ALIA_PP_CONCAT1(a, b)
#define ALIA_PP_CONCAT1(a, b) ALIA_PP_CONCAT2(a, b)
#define ALIA_PP_CONCAT2(a, b) a##b

#define ALIA_PP_FE_2_0(F, a, b)
#define ALIA_PP_FE_2_1(F, a, b, x) F(a, b, x)
#define ALIA_PP_FE_2_2(F, a, b, x, ...) \
F(a, b, x) ALIA_PP_FE_2_1(F, a, b, __VA_ARGS__)
#define ALIA_PP_FE_2_3(F, a, b, x, ...) \
F(a, b, x) ALIA_PP_FE_2_2(F, a, b, __VA_ARGS__)
#define ALIA_PP_FE_2_4(F, a, b, x, ...) \
F(a, b, x) ALIA_PP_FE_2_3(F, a, b, __VA_ARGS__)
#define ALIA_PP_FE_2_5(F, a, b, x, ...) \
F(a, b, x) ALIA_PP_FE_2_4(F, a, b, __VA_ARGS__)

#define ALIA_PP_GET_MACRO(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
#define ALIA_PP_FOR_EACH_2(F, a, b, ...) \
ALIA_PP_GET_MACRO( \
_0, \
__VA_ARGS__, \
ALIA_PP_FE_2_5, \
ALIA_PP_FE_2_4, \
ALIA_PP_FE_2_3, \
ALIA_PP_FE_2_2, \
ALIA_PP_FE_2_1, \
ALIA_PP_FE_2_0) \
(F, a, b, __VA_ARGS__)

#define ALIA_DEFINE_STRUCT_SIGNAL_FIELD(signal_type, struct_name, field_name) \
auto ALIA_PP_CONCAT(ALIA_PP_CONCAT(_get_, field_name), _signal)() \
{ \
return (*this)->*&struct_name::field_name; \
} \
__declspec(property( \
get = ALIA_PP_CONCAT(ALIA_PP_CONCAT(_get_, field_name), _signal))) \
alia::field_signal< \
ALIA_PP_CONCAT(ALIA_PP_CONCAT(signal_type, _), struct_name), \
decltype(struct_name::field_name)> \
field_name;

#define ALIA_DEFINE_STRUCT_SIGNAL_FIELDS(signal_type, struct_name, ...) \
ALIA_PP_FOR_EACH_2( \
ALIA_DEFINE_STRUCT_SIGNAL_FIELD, \
signal_type, \
struct_name, \
__VA_ARGS__)

#define ALIA_DEFINE_CUSTOM_STRUCT_SIGNAL( \
signal_name, signal_type, struct_name, ...) \
struct signal_name : alia::signal_type<struct_name> \
{ \
using signal_ref::signal_ref; \
ALIA_DEFINE_STRUCT_SIGNAL_FIELDS( \
signal_type, struct_name, __VA_ARGS__) \
};

#define ALIA_DEFINE_STRUCT_SIGNAL(signal_type, struct_name, ...) \
ALIA_DEFINE_CUSTOM_STRUCT_SIGNAL( \
ALIA_PP_CONCAT(ALIA_PP_CONCAT(signal_type, _), struct_name), \
signal_type, \
struct_name, \
__VA_ARGS__)

#define ALIA_DEFINE_STRUCT_SIGNALS(struct_name, ...) \
ALIA_DEFINE_STRUCT_SIGNAL(readable, struct_name, __VA_ARGS__) \
ALIA_DEFINE_STRUCT_SIGNAL(duplex, struct_name, __VA_ARGS__)

#endif

#endif
79 changes: 79 additions & 0 deletions unit_tests/signals/field_macros.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <alia/signals/field_macros.hpp>

#include <testing.hpp>

#include <alia/signals/basic.hpp>
#include <alia/signals/lambdas.hpp>

// __declspec(property(...)) is non-standard.
#if defined(__clang__) || defined(_MSC_VER)

namespace {

struct foo
{
int x;
std::string y;
};

ALIA_DEFINE_STRUCT_SIGNALS(foo, x, y)

} // namespace

TEST_CASE("ALIA_DEFINE_STRUCT_SIGNALS", "[signals][field_macros]")
{
using namespace alia;

foo f = {2, "1.5"};
auto f_signal = lambda_duplex(
always_has_value,
[&]() { return f; },
always_ready,
[&](foo const& v) { f = v; },
[&]() { return combine_ids(make_id(f.x), make_id(f.y)); });

REQUIRE((std::is_same<readable_foo::value_type, foo>::value));
REQUIRE(signal_is_readable<readable_foo>::value);
REQUIRE(!signal_is_writable<readable_foo>::value);

readable_foo rf = f_signal;

REQUIRE(signal_has_value(rf.x));
REQUIRE(read_signal(rf.x) == 2);

REQUIRE((std::is_same<duplex_foo::value_type, foo>::value));
REQUIRE(signal_is_readable<duplex_foo>::value);
REQUIRE(signal_is_writable<duplex_foo>::value);

duplex_foo df = f_signal;

typedef decltype(df.x) x_signal_t;
REQUIRE((std::is_same<x_signal_t::value_type, int>::value));
REQUIRE(signal_is_readable<x_signal_t>::value);
REQUIRE(signal_is_writable<x_signal_t>::value);

REQUIRE(signal_has_value(df.x));
REQUIRE(read_signal(df.x) == 2);
REQUIRE(signal_ready_to_write(df.x));
write_signal(df.x, 1);
REQUIRE(f.x == 1);

auto x_signal = df.x;
auto y_signal = df.y;

typedef decltype(y_signal) y_signal_t;
REQUIRE((std::is_same<y_signal_t::value_type, std::string>::value));
REQUIRE(signal_is_readable<y_signal_t>::value);
REQUIRE(signal_is_writable<y_signal_t>::value);

REQUIRE(y_signal.value_id() != x_signal.value_id());
REQUIRE(signal_has_value(y_signal));
REQUIRE(read_signal(y_signal) == "1.5");
REQUIRE(signal_ready_to_write(y_signal));
captured_id original_y_id = y_signal.value_id();
write_signal(y_signal, "0.5");
REQUIRE(y_signal.value_id() != original_y_id.get());
REQUIRE(f.y == "0.5");
}

#endif

0 comments on commit 74b860c

Please sign in to comment.