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

Watchable base connections are not moved #122

Open
lkotsonis opened this issue Oct 31, 2021 · 5 comments
Open

Watchable base connections are not moved #122

lkotsonis opened this issue Oct 31, 2021 · 5 comments

Comments

@lkotsonis
Copy link

I was trying to do sth like this (more objects are actually involved inside the component, plus some functionality for my Component, irrelevant to the issue though):

class Component
{
private:
lager::reader<float> m_reader;

public:
    Component(lager::reader<float>  float_reader)
    : m_reader(float_reader)
    {}
    Component(Component &&) = default;
    Component& operator=(Component&&) = default;
    Component(const Component &) = default;
    Component& operator=(const Component&) = default;
};

struct Model
{
    float value;
};

int main()
{
    // ... setup store etc..
    lager::reader<float> reader = store[&Model::value];
    reader.watch([](auto new_value) { std::cout << "new value!" << std::endl; });
    Component c(reader);
    // dispatch change in model's value variable and expect cout of "new value!"... nothing happens
}

but apparently watchable_base does not move connections when the move/copy constructor/assignment is called. Is this on purpose? Or maybe I got this all wrong, any input would be appreciated. Thank you!

@arximboldi
Copy link
Owner

It is correct that the watchable_base does not move connections. On the other hand, the connections survive reassignments. That's on purpose (the connections are local to the object but not global to the signal).

I think however in your example the connection survives because the reader is in scope until the end of main. Maybe with a more precise example I can suggest design alternatives.

@lkotsonis
Copy link
Author

lkotsonis commented Nov 2, 2021

It is correct that the watchable_base does not move connections. On the other hand, the connections survive reassignments. That's on purpose (the connections are local to the object but not global to the signal).

I think however in your example the connection survives because the reader is in scope until the end of main. Maybe with a more precise example I can suggest design alternatives.

Thanks for the prompt reply!

I understand now, the example is indeed not showing the issue. Maybe changing the main to this would be more descriptive of what I am trying to do (I used the main from the counter example). The case below (combined with the class component above), although oversimplified, actually makes the issue manifest:

int main() {
  auto store = lager::make_store<counter::action>(
      counter::model{}, lager::with_manual_event_loop{});
  watch(store, draw);

  std::vector<counter::Component> vec;

  {
    lager::reader<int> reader = store[&counter::model::value];
    reader.watch(
        [](auto new_value) { std::cout << "new value!" << std::endl; });

    vec.emplace_back(reader);
  }

  auto event = char{};
  while (std::cin >> event) {
    if (auto act = intent(event)) store.dispatch(*act);
  }
}

This is more or less my use-case (or imagine having a vector of std::variant components to make it more pragmatic).

This is all stemming from a need to marry value-oriented design with traditional audio DSP classes where there is usually a need for an encapsulated state (which I cannot change), for which I had to create a layer of abstraction that ties together a lager::reader with one of those audio DSP classes. Any feedback or ideas would be greatly appreciated.

@lkotsonis
Copy link
Author

lkotsonis commented Nov 5, 2021

Well, there was an obvious solution that I hadn't tried, namely wrapping a smart pointer around the objects in the vector. That actually mitigates the issue. May I ask what is the reasoning behind a connection not carried over when a reader object is moved/copied?

@arximboldi
Copy link
Owner

I see! Yes I have a similar case. One solution that I have applied in the past is to override moving in my class that contains the readers, and rebind watchers after move. Maybe not entirely satisfying.

The current behavior is there because readers are passed around a lot, and normally you pass it to someone that is interested in the data, but the watchers are local to your own "stateful" business.

I leave this open because admittedly I would like to do something around this, since it seems like everyone is getting confused about this, and there cases, like this, where the current behavior is actually annoying...

@arximboldi
Copy link
Owner

I have some ideas, that actually could be API breaking changes...

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