Skip to content

Commit

Permalink
Macro Madness (#69)
Browse files Browse the repository at this point in the history
* first wave of macro magic

* Macro Reactions

* adding physical and logical actions

* fixing problem with actions

* clang format

* merging construct and define

* unifing api of timers

* default entry point

* new way to express reactions
  • Loading branch information
tanneberger authored Oct 17, 2024
1 parent 4c3c0da commit bba5cd0
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 426 deletions.
157 changes: 157 additions & 0 deletions include/reactor-uc/macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,163 @@
((Connection *)&(conn))->upstream = (Port *)&(up); \
} while (0)

// Convenience macro to register upstream and downstream on a connection
#define CONNECT(ConnectionVariable, SourcePort, DestinationPort) \
CONN_REGISTER_UPSTREAM(ConnectionVariable, SourcePort); \
CONN_REGISTER_DOWNSTREAM(ConnectionVariable, DestinationPort)

typedef struct Output Output;

#define DEFINE_OUTPUT_PORT(PortName, SourceSize) \
typedef struct { \
Output super; \
Reaction *sources[(SourceSize)]; \
} PortName; \
\
void PortName##_ctor(PortName *self, Reactor *parent) { \
Output_ctor(&self->super, parent, self->sources, SourceSize); \
}

typedef struct Input Input;

#define DEFINE_INPUT_PORT(PortName, EffectSize, BufferType, BufferSize) \
typedef struct { \
Input super; \
Reaction *effects[(EffectSize)]; \
BufferType buffer[(BufferSize)]; \
} PortName; \
\
void PortName##_ctor(PortName *self, Reactor *parent) { \
Input_ctor(&self->super, parent, self->effects, (EffectSize), self->buffer, sizeof(self->buffer[0])); \
}

typedef struct Timer Timer;

#define DEFINE_TIMER(TimerName, EffectSize, Offset, Period) \
typedef struct { \
Timer super; \
Reaction *effects[(EffectSize)]; \
} TimerName; \
\
void TimerName##_ctor(TimerName *self, Reactor *parent) { \
Timer_ctor(&self->super, parent, Offset, Period, self->effects, EffectSize); \
}

typedef struct Reaction Reaction;

#define DEFINE_REACTION(ReactorName, ReactionIndex, EffectSize) \
typedef struct { \
Reaction super; \
Trigger *effects[(EffectSize)]; \
} ReactorName##_##ReactionIndex;

#define REACTION_BODY(ReactorName, ReactionIndex, ReactionBody) \
void ReactorName##_body_##ReactionIndex(Reaction *_self) { \
ReactorName *self = (ReactorName *)_self->parent; \
Environment *env = self->super.env; \
ReactionBody \
} \
void ReactorName##_##ReactionIndex##_ctor(ReactorName##_##ReactionIndex *self, Reactor *parent) { \
Reaction_ctor(&self->super, parent, ReactorName##_body_##ReactionIndex, self->effects, \
sizeof(self->effects) / sizeof(self->effects[0]), ReactionIndex); \
}

typedef struct Startup Startup;

#define DEFINE_STARTUP(StartupName, EffectSize) \
typedef struct { \
Startup super; \
Reaction *effects[(EffectSize)]; \
} StartupName; \
\
void StartupName##_ctor(StartupName *self, Reactor *parent) { \
Startup_ctor(&self->super, parent, self->effects, sizeof(self->effects) / sizeof(self->effects[0])); \
}

typedef struct Shutdown Shutdown;

#define DEFINE_SHUTDOWN(ShutdownName, EffectSize) \
typedef struct { \
Shutdown super; \
Reaction *effects[(EffectSize)]; \
} ShutdownName; \
\
void ShutdownName##_ctor(ShutdownName *self, Reactor *parent) { \
Shutdown_ctor(&self->super, parent, self->effects, sizeof(self->effects) / sizeof(self->effects[0])); \
}

typedef struct LogicalAction LogicalAction;

#define DEFINE_LOGICAL_ACTION(ActionName, EffectSize, SourceSize, BufferTyp, BufferSize, Offset, Spacing) \
typedef struct { \
LogicalAction super; \
BufferTyp buffer[(BufferSize) + 1]; \
Reaction *sources[(SourceSize)]; \
Reaction *effects[(EffectSize)]; \
} ActionName; \
\
void ActionName##_ctor(ActionName *self, Reactor *parent) { \
LogicalAction_ctor(&self->super, Offset, Spacing, parent, self->sources, \
sizeof(self->sources) / sizeof(self->sources[0]), self->effects, \
sizeof(self->effects) / sizeof(self->effects[0]), &self->buffer, sizeof(self->buffer[0]), \
sizeof(self->buffer) / sizeof(self->buffer[0])); \
}

typedef struct PhysicalAction PhysicalAction;

#define DEFINE_PHYSICAL_ACTION(ActionName, EffectSize, SourceSize, BufferTyp, BufferSize, Offset, Spacing) \
typedef struct { \
PhysicalAction super; \
BufferTyp buffer[(BufferSize)]; \
Reaction *sources[(SourceSize)]; \
Reaction *effects[(EffectSize)]; \
} ActionName; \
\
void ActionName##_ctor(ActionName *self, Reactor *parent) { \
PhysicalAction_ctor(&self->super, Offset, Spacing, parent, self->sources, \
sizeof(self->sources) / sizeof(self->sources[0]), self->effects, \
sizeof(self->effects) / sizeof(self->effects[0]), &self->buffer, sizeof(self->buffer[0]), \
sizeof(self->buffer) / sizeof(self->buffer[0])); \
}

typedef struct LogicalConnection LogicalConnection;

#define DEFINE_LOGICAL_CONNECTION(ConnectionName, DownstreamSize) \
typedef struct { \
LogicalConnection super; \
Input *downstreams[(DownstreamSize)]; \
} ConnectionName; \
\
void ConnectionName##_ctor(ConnectionName *self, Reactor *parent) { \
LogicalConnection_ctor(&self->super, parent, (Port **)self->downstreams, \
sizeof(self->downstreams) / sizeof(self->downstreams[0])); \
}

typedef struct DelayedConnection DelayedConnection;

#define DEFINE_DELAYED_CONNECTION(ConnectionName, DownstreamSize, BufferType, BufferSize, Delay) \
typedef struct { \
DelayedConnection super; \
BufferType buffer[(BufferSize)]; \
Input *downstreams[(BufferSize)]; \
} ConnectionName; \
\
void ConnectionName##_ctor(ConnectionName *self, Reactor *parent) { \
DelayedConnection_ctor(&self->super, parent, (Port **)self->downstreams, \
sizeof(self->downstreams) / sizeof(self->downstreams[0]), Delay, self->buffer, \
sizeof(self->buffer[0]), sizeof(self->buffer) / sizeof(self->buffer[0])); \
}

#define ENTRY_POINT(MainReactorName) \
void lf_start() { \
MainReactorName main_reactor; \
Environment env; \
Environment_ctor(&env, (Reactor *)&main_reactor); \
MyReactor_ctor(&main_reactor, &env); \
env.assemble(&env); \
env.start(&env); \
}

// TODO: The following macro is defined to avoid compiler warnings. Ideally we would
// not have to specify any alignment on any structs. It is a TODO to understand exactly why
// the compiler complains and what we can do about it.
Expand Down
60 changes: 16 additions & 44 deletions test/unit/action_microstep_test.c
Original file line number Diff line number Diff line change
@@ -1,50 +1,23 @@
#include "reactor-uc/reactor-uc.h"
#include "unity.h"

typedef struct {
LogicalAction super;
int buffer[1];

Reaction *sources[1];
Reaction *effects[1];
} MyAction;

typedef struct MyStartup MyStartup;

struct MyStartup {
Startup super;
Reaction *effects_[1];
};
DEFINE_LOGICAL_ACTION(MyAction, 1, 1, int, 1, MSEC(0), MSEC(0));
DEFINE_STARTUP(MyStartup, 1);
DEFINE_REACTION(MyReactor, 0, 1);

typedef struct {
Reaction super;
Trigger *effects[1];
} MyReaction;

struct MyReactor {
Reactor super;
MyReaction my_reaction;
MyReactor_0 my_reaction;
MyAction my_action;
MyStartup startup;
Reaction *_reactions[1];
Trigger *_triggers[2];
int cnt;
};

void MyAction_ctor(MyAction *self, struct MyReactor *parent) {
LogicalAction_ctor(&self->super, MSEC(0), MSEC(0), &parent->super, self->sources, 1, self->effects, 1, &self->buffer,
sizeof(self->buffer[0]), 2);
}
} MyReactor ;

void MyStartup_ctor(struct MyStartup *self, Reactor *parent, Reaction *effects) {
self->effects_[0] = effects;
Startup_ctor(&self->super, parent, self->effects_, 1);
}

void action_handler(Reaction *_self) {
struct MyReactor *self = (struct MyReactor *)_self->parent;
Environment *env = self->super.env;
REACTION_BODY(MyReactor, 0, {
MyAction *my_action = &self->my_action;

if (self->cnt == 0) {
TEST_ASSERT_EQUAL(lf_is_present(my_action), false);
} else {
Expand All @@ -66,28 +39,27 @@ void action_handler(Reaction *_self) {
if (self->cnt < 100) {
lf_schedule(my_action, ++self->cnt, 0);
}
}
})

void MyReaction_ctor(MyReaction *self, Reactor *parent) {
Reaction_ctor(&self->super, parent, action_handler, self->effects, 1, 0);
}

void MyReactor_ctor(struct MyReactor *self, Environment *env) {
void MyReactor_ctor(MyReactor *self, Environment *env) {
self->_reactions[0] = (Reaction *)&self->my_reaction;
self->_triggers[0] = (Trigger *)&self->startup;
self->_triggers[1] = (Trigger *)&self->my_action;

Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 2);
MyAction_ctor(&self->my_action, self);
MyReaction_ctor(&self->my_reaction, &self->super);
MyStartup_ctor(&self->startup, &self->super, &self->my_reaction.super);
MyAction_ctor(&self->my_action, &self->super);
MyReactor_0_ctor(&self->my_reaction, &self->super);
MyStartup_ctor(&self->startup, &self->super);

ACTION_REGISTER_EFFECT(self->my_action, self->my_reaction);
REACTION_REGISTER_EFFECT(self->my_reaction, self->my_action);
ACTION_REGISTER_SOURCE(self->my_action, self->my_reaction);
STARTUP_REGISTER_EFFECT(self->startup, self->my_reaction);
self->cnt = 0;
}

void test_simple() {
struct MyReactor my_reactor;
MyReactor my_reactor;
Environment env;
Environment_ctor(&env, (Reactor *)&my_reactor);
MyReactor_ctor(&my_reactor, &env);
Expand Down
57 changes: 14 additions & 43 deletions test/unit/action_test.c
Original file line number Diff line number Diff line change
@@ -1,48 +1,21 @@
#include "reactor-uc/reactor-uc.h"
#include "unity.h"

typedef struct {
LogicalAction super;
int buffer[2];

Reaction *sources[1];
Reaction *effects[1];
} MyAction;

typedef struct MyStartup MyStartup;

struct MyStartup {
Startup super;
Reaction *effects_[1];
};
DEFINE_LOGICAL_ACTION(MyAction, 1, 1, int, 2, MSEC(0), MSEC(0));
DEFINE_STARTUP(MyStartup, 1);
DEFINE_REACTION(MyReactor, 0, 1);

typedef struct {
Reaction super;
Trigger *effects[1];
} MyReaction;

struct MyReactor {
Reactor super;
MyReaction my_reaction;
MyReactor_0 my_reaction;
MyAction my_action;
MyStartup startup;
Reaction *_reactions[1];
Trigger *_triggers[2];
int cnt;
};

void MyAction_ctor(MyAction *self, struct MyReactor *parent) {
LogicalAction_ctor(&self->super, MSEC(0), MSEC(0), &parent->super, self->sources, 1, self->effects, 1, &self->buffer,
sizeof(self->buffer[0]), 2);
}
} MyReactor ;

void MyStartup_ctor(struct MyStartup *self, Reactor *parent, Reaction *effects) {
self->effects_[0] = effects;
Startup_ctor(&self->super, parent, self->effects_, 1);
}

void action_handler(Reaction *_self) {
struct MyReactor *self = (struct MyReactor *)_self->parent;
REACTION_BODY(MyReactor, 0, {
MyAction *my_action = &self->my_action;
if (self->cnt == 0) {
TEST_ASSERT_EQUAL(lf_is_present(my_action), false);
Expand All @@ -57,28 +30,26 @@ void action_handler(Reaction *_self) {
}

lf_schedule(my_action, ++self->cnt, MSEC(100));
}
})

void MyReaction_ctor(MyReaction *self, Reactor *parent) {
Reaction_ctor(&self->super, parent, action_handler, self->effects, 1, 0);
}

void MyReactor_ctor(struct MyReactor *self, Environment *env) {
void MyReactor_ctor(MyReactor *self, Environment *env) {
self->_reactions[0] = (Reaction *)&self->my_reaction;
self->_triggers[0] = (Trigger *)&self->startup;
self->_triggers[1] = (Trigger *)&self->my_action;
Reactor_ctor(&self->super, "MyReactor", env, NULL, NULL, 0, self->_reactions, 1, self->_triggers, 2);
MyAction_ctor(&self->my_action, self);
MyReaction_ctor(&self->my_reaction, &self->super);
MyStartup_ctor(&self->startup, &self->super, &self->my_reaction.super);
MyAction_ctor(&self->my_action, &self->super);
MyReactor_0_ctor(&self->my_reaction, &self->super);
MyStartup_ctor(&self->startup, &self->super);
ACTION_REGISTER_EFFECT(self->my_action, self->my_reaction);
REACTION_REGISTER_EFFECT(self->my_reaction, self->my_action);
ACTION_REGISTER_SOURCE(self->my_action, self->my_reaction);
STARTUP_REGISTER_EFFECT(self->startup, self->my_reaction);

self->cnt = 0;
}

void test_simple() {
struct MyReactor my_reactor;
MyReactor my_reactor;
Environment env;
Environment_ctor(&env, (Reactor *)&my_reactor);
MyReactor_ctor(&my_reactor, &env);
Expand Down
Loading

0 comments on commit bba5cd0

Please sign in to comment.