0

Context:

I'm working on an embedded system with many small functional modules. The business logic within each module is made as simple as possible, but those modules generally need to gather bits of information from other places within the system to do their work.

So far, to support unit testing and generally break dependencies among the modules, I've been passing in functions that promise to deliver some value, e.g.:

typedef time_t (*system_time_fn_t)(void);

static time_t s_system_time_fn;

void my_module_init(system_time_fn_t fn) {
   s_system_time_fn = fn;           // save pointer to system time function
}

int my_module_operation(void) {
  time_t now = s_system_time_fn();  // call function to get system time.
  ... do some stuff ...
  return result;
}

The problem

This form of dependency injection works fine for modules that only need a few bits of information from other modules, but sometimes a module really needs to gather many bits of info from other places in the system.

In these cases, passing in lots of functions becomes unwieldy.

Proposed solution

I'm considering a "bulletin board" model, where a module can read inputs from slots in the bulletin board and write its outputs to other slots, perhaps like this:

void my_module_operation(void) {
  time_t now = bboard_read(BB_SYSTEM_TIME);
  ... do some stuff ...
  bboard_write(BB_MY_MODULE_RESULT, result);
}

I realize a bulletin board model has the potential for devolving into something as ugly as a zillion global variables, but I promise to be disciplined about its use. And besides, it makes it really clear how to write unit tests for each module.

Big note:

This system is written in pure C: so no virtual functions, no OOP, no thunks, no late binding.

The questions:

Is this a viable approach? What has been your experience with it? Pitfalls? Advantages?

6
  • 1
    My two cents: A potential issue is that the bulletin board is essentially global state; now, the problem with global state is not that it's global, it's that if there are no architectural guidelines, it gets accessed haphazardly in an unstructured way, so you get subtle and unexpected coupling, no clear separation of concerns, things become hard to consider and/or test in isolation, etc. So IMO along with your bulletin board you'd have to come up with some structured way (not necessarily enforceable by the compiler) of using it so that you minimize unwanted coupling. Dec 20, 2020 at 20:28
  • E.g., in game dev, ECS essentially has global state - a bunch of components - but they manipulate them via "systems" that have fairly clear-cut responsibilities, and developers often follow a set of rules/heuristics on how to write good ECS code. Redux has a global store for state, but React components don't access it directly; instead they interact with it by dispatching actions, etc. Dec 20, 2020 at 20:28
  • 1
    @FilipMilovanović: +1. Thus my comment that "I realize a bulletin board model has the potential for devolving into something as ugly as a zillion global variables, but I promise to be disciplined about its use" For example, only one module would be allowed to write to a bboard slot. Dec 20, 2020 at 20:33
  • P.S. BTW, don't get me wrong - I'm not saying you need to introduce a complicated framework that would handle this, but rather to think more carefully about how client code wold use your bulletin board, what its API would look like, etc, and to keep these considerations in mind as you go along. Dec 20, 2020 at 20:40
  • This sounds a little bit like the tuple spaces from Linda. Dec 20, 2020 at 21:53

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.