aui::PropertyModifier#
Temporary transparent object that gains write access to underlying property's value, notifying about value changes when destructed.
Header: | #include <AUI/Common/PropertyModifier.h> |
CMake: | aui_link(my_target PUBLIC aui::core) |
Detailed Description#
PropertyModifier is a result of writeScope()
method of writeable properties. Also, it is used inside non-const
operator implementations (see below). It gains transparent writeable handle to property's value, and calls notify()
method on associated property upon PropertyModifier destruction.
Non-const operators of properties such as non-const versions of operator=
, operator+=
, operator-=
have a side
effect of emitting changed
signal upon operation completion. This ensures that modifying access to the property can
be observed.
AProperty<int> counter = 0;
AObject::connect(counter.changed, AUI_SLOT(observer)::observeInt);
EXPECT_CALL(observer, observeInt(1)).Times(1);
counter += 1; // observable by observeInt
AProperty<AString> name = "Hello";
AObject::connect(name.changed, AUI_SLOT(observer)::observeString);
EXPECT_CALL(observer, observeString("Hello world"_as)).Times(1);
name += " world"; // observable by observeString
Note
Make sure your read-only operators (such as operator+
, operator-
) have marked const, otherwise property
would treat them as a writing access, resulting in unwanted signaling changed
upon each access.
AProperty<int> counter = 0;
AObject::connect(counter.changed, AUI_SLOT(observer)::observeInt);
EXPECT_CALL(observer, observeInt(testing::_)).Times(0);
int nextCounter = counter + 1; // read-only access; noone is notified
Member access operator (operator->)#
operator->
is a special case. operator->
having both non-const and const versions is a common practice, so
there's should be a way to distinguish between non-const access and const access, preferring the latter if possible.
The const version of operator->
can be used directly on property:
EXPECT_CALL(observer, observeString(testing::_)).Times(0);
AProperty<AString> name = "Hello";
AObject::connect(name.changed, AUI_SLOT(observer)::observeString);
// returns const pointer
auto data = name->data();
+=
, -=
):
AProperty<AString> name = "Hello";
AObject::connect(name.changed, AUI_SLOT(observer)::observeString);
EXPECT_CALL(observer, observeString("Test"_as)).Times(1);
name = "Test";
operator->
. To do this, you need a
aui::PropertyModifier
object that grants such access:
AProperty<AString> name = "Hello";
AObject::connect(name.changed, AUI_SLOT(observer)::observeString);
EXPECT_CALL(observer, observeString("Hell"_as)).Times(1);
name.writeScope()->removeAll('o');
You need to be careful when performing multiple operations at once. Design rationale behind writeScope()
method
makes it painful (by intention) performing multiple accesses, since it would lead to unwanted change notifications
during the process:
// WRONG WAY
EXPECT_CALL(observer, observeString("Hell"_as)).Times(1);
name.writeScope()->removeAll('o');
EXPECT_CALL(observer, observeString("He"_as)).Times(1);
name.writeScope()->removeAll('l');
The right way is to create aui::PropertyModifier
just once. This will produce exactly one notification, ensuring
that modifications to the property are performed atomically. This means that all operations within
the scope of aui::PropertyModifier
produced by writeScope()
will be treated as one unit, and only one change
notification will be emitted.