Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More fine-grained event handling #4762

Open
RudolfWeeber opened this issue Jul 25, 2023 · 1 comment
Open

More fine-grained event handling #4762

RudolfWeeber opened this issue Jul 25, 2023 · 1 comment

Comments

@RudolfWeeber
Copy link
Contributor

No description provided.

@jngrad
Copy link
Member

jngrad commented Apr 8, 2024

We currently rely on pre-change resp. post-change hooks to let components of the system react to a proposed change, using functions named veto_*_change() resp. on_*_change(). The former typically throw an error to prevent a change that would leave the system in an undefined state, whereas the latter allow the component to react to a change, such as re-tuning long-range solvers parameters.

These methods sometimes take arguments, such as the proposed change to check if the value would lead to an undefined state, or a flag to help skip some checks when the corresponding pre-conditions were already validated upstream.

This mechanism is cumbersome and inflexible. In particular, each event needs a dedicated method, sometimes two if both pre- and post-change hooks are needed. These methods need to appear in all derived class, with the correct flags (which can be optional arguments).

To improve separation of concerns and the open/close principle, we could rewrite these hooks using a message-passing interface. Below is a prototype for this new design. This change is made possible by #4816, which encapsulated all event hooks into the core System class.

#include <any>
#include <functional>
#include <optional>
#include <stdexcept>

struct OnChange {
  enum class Property : int {
    none = 0,
    box_l,
    time_step,
  };
  enum class When : int {
    none = 0,
    before,
    after,
  };
  OnChange(Property property_, When when_, std::any &&value_) :
    property{property_}, when{when_}, value{value_}, callback(std::nullopt) {}
  Property property;
  When when;
  std::any value;
  std::optional<std::function<void()>> callback;
};

class Solver {
public:
  void on_change(OnChange const &message) {
    if (message.when == OnChange::When::before) {
      if (message.property == OnChange::Property::time_step) {
        auto const time_step = std::any_cast<double>(message.value);
        if (time_step /*...*/) {
          throw std::runtime_error("time step too small for feature Solver");
        }
      }
    }
  }
};

class System {
public:
  double box_l;
  double time_step;
  Solver *solver;
  void set_time_step(double value) {
    if (solver) {
      auto message = OnChange(OnChange::Property::time_step, OnChange::When::before, value);
      solver->on_change(message);
      this->time_step = value;
      message.when = OnChange::When::after;
      solver->on_change(message);
    }
  }
};

int main() {
  Solver solver{};
  System system{10., 0.01, &solver};
  system.set_time_step(0.05);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants