Skip to content

Commit

Permalink
Delete ambiguous assigment operators in state
Browse files Browse the repository at this point in the history
It is possible to assign a state with the new value, which implicitly
creates a new state and replaces the current one. It basically detaches
all the cursors and listeners.

Alternatively, the problem can be solved by making the constructor
explicit, but it may create some inconviniences when using the state.

fixes #135
  • Loading branch information
dimula73 committed Feb 14, 2022
1 parent 8877e86 commit 4630771
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 0 deletions.
7 changes: 7 additions & 0 deletions lager/state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ class state : public cursor_base<detail::state_node<T, TagT>>

state(state&&) = default;
state& operator=(state&&) = default;

/*!
* Deletes implicit creation and replacement of the store
* when it is assigned with the base type
*/
state& operator=(const T&) = delete;
state& operator=(T&&) = delete;
};

template <typename T>
Expand Down
64 changes: 64 additions & 0 deletions test/state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
// or here: <https://github.com/arximboldi/lager/blob/master/LICENSE>
//

#include <boost/operators.hpp>

#include <catch.hpp>

#include <lager/state.hpp>
Expand Down Expand Up @@ -183,3 +185,65 @@ TEST_CASE("state, capsule carries its own watchers")
CHECK(1 == s.count());
CHECK(sig->observers().empty());
}

namespace TestNS {
struct Child : public boost::equality_comparable<Child>
{
friend bool operator==(const Child &lhs, const Child &rhs) {
return lhs.childValue == rhs.childValue;
}

Child() = default;
Child(int value) : childValue(value) {}

int childValue = 0;
};

struct Parent : public boost::equality_comparable<Parent>
{
friend bool operator==(const Parent &lhs, const Parent &rhs) {
return lhs.parentValue == rhs.parentValue &&
lhs.child == rhs.child;
}

int parentValue = 0;
Child child;
};
}

TEST_CASE("state, check notifications after direct assignment to state")
{

lager::state<TestNS::Parent, automatic_tag> state;
lager::cursor<TestNS::Child> childCursor = state[&TestNS::Parent::child];
lager::cursor<int> childValueCursor = childCursor[&TestNS::Child::childValue];

auto childSpy = testing::spy([&](const TestNS::Child &value) {
CHECK(value.childValue == 13);
});

auto parentSpy = testing::spy([&](const TestNS::Parent &value) {
CHECK(value.parentValue == 0);
CHECK(value.child.childValue == 13);
});

watch(state, parentSpy);
watch(childCursor, childSpy);

/// this code must be impossible to compile,
/// because it implicitly replaces the store,
/// breaking all the links
///
/// state = TestNS::Parent();

// use explicit setters instead (like in cursors)
state.set(TestNS::Parent());

childValueCursor.set(13);

CHECK(state.get().parentValue == 0);
CHECK(state.get().child.childValue == 13);

CHECK(childSpy.count() == 1);
CHECK(parentSpy.count() == 1);
}

0 comments on commit 4630771

Please sign in to comment.