Discussion:
RFC: RAII for property changes
(too old to reply)
André Somers
2015-04-15 09:49:56 UTC
Permalink
Hi,

When writing QObject-derived classes with properties, I often found
myself writing code like this:

void MyClass::setFoo(QString value)
{
if (m_foo != value) {
m_foo = value;
emit fooChanged(m_foo);
}
}

Trivial enough, in the simple case. However, when more than one property
is involved at the same time, or properties depend on each other, things
can get way less simple and more error-phrone quickly, I found. If the
order of signals matters (and I found it does some times!), it gets even
more tricky to get things right. This is especially tricky because you
really would like to emit the signal at the moment the class is in a
consistent state. That is, if you have a method that sets both width and
height of an object at the same time, you really don't want to emit the
signal about the change of one property before the change of another
property is also set. In this case, the change may be harmless (but
potentially expensive, as perhaps two relayouts are triggered when only
one was needed), but there are cases where you'd really expose the class
in an inconsistent state if you emit too soon. Considder that at the
moment you emit, you give other code the change to do whatever its wants
again, including calling methods on your class, even the very same
method they just called, or worse, delete it... Your class better be in
a consistent state when they do such things.

That's why I have been working with a RAII implementation to monitor
property changes for a while now. The idea is that you instantiate a
RAII class to monitor a property at the beginning of a method that _may_
change the property value, and then do all you want. Upon destruction of
the RAII class, it checks if the property value changed and if it did,
emit the associated signal.

The code I now write looks like this:

void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);

m_foo = value;
}

which then is further simplified by creating a small macro called
guardProperty to this:

void MyClass::setFoo(QString value)
{
guardProperty(foo); //foo is the name of the Q_PROPERTY
m_foo = value;
}

Note that you can guard as many properties as you need:

void MyClass::setFoo(QString value)
{
guardProperty(foo); //foo is the name of the Q_PROPERTY
guardProperty(isValid); //the isValid property depends on m_foo as well

m_foo = value;
}


The PropertyGuard class itself is quite simple in its basic form. It
uses QMetaObject to read the current value and find the notify signal
for the property. Upon desctruction, it compares the current value to
the initial value stored at creation, and if the two don't match, the
signal is emitted. The code deals with signals with zero or one
arguments, assuming that in the latter case the one argument is the new
property value.

I found this little device to be very useful in practice. Not so much in
trivial setters like the ones on top (but still useful there, as the
intent is clearer and there is less typing), but very much so in
non-trivial cases where more than one property may change at a time or
other complex interactions take place. In cases where a method changing
should trigger other property changes as well, while not exposing
inconsistent states of the class I found the device to be extremely
valuable, as I can be sure that the emits are done at a moment where the
state of class is fully consistent. I find myself writing emit less and
less.


So, what are your opinions on using a mechanism like this? I have not
found it in Qt itself, but perhaps others here are already using similar
methods or perhaps very different methods to tackle the issues I described?

Looking forward to your comments,


André Somers
Nils Jeisecke
2015-04-15 11:56:36 UTC
Permalink
Hi,
Post by André Somers
That's why I have been working with a RAII implementation to monitor
property changes for a while now.
This looks like an interesting idea.

Another issue I often have with properties is performance related.
Imagine a model that has more than one filter property. In case that
two properties should be set, it does not make sense to update the
model twice. Adding a special setFilters method with multiple
arguments is no option if you want to use property binding in QML.

So I usually have a private "update" slot and a timer that's restarted
on each property invocation so that the model update will take place
on the next event loop iteration.

That "guard" could have a second parameter specifying a "property
evaluation" slot that is guaranteed to be invoked *once* on the next
event loop iteration. It might be tricky to manage the timer though,
maybe setting a special custom object property
"__<SLOT_NAME>_invoke_timer" storing the timer pointer could help? Or
- to get rid of the expensive timer - a flag could be stored as a
property controlling whether QMetaObject::invoke(this, "<SLOT_NAME>",
Qt::QueuedConnection) should be called - the slot (or a c++11 lambda?)
would have to reset that flag then.

Thinking about this, maybe this functionality should be implemented in
a separate class.

Nils
Marc Mutz
2015-04-15 14:43:50 UTC
Permalink
Hi André,
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.

For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt Experts
Keith Gardner
2015-04-15 14:54:45 UTC
Permalink
Hi André,
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.
Thanks,
Marc
I have actually run into the same situation and made a template class that
owns the variable. Its constructor takes an initial value and a
std::function<void (const T&)> as a callback for when the value changes.
The callback can be a lambda or a std::bind to the expected signal. I also
added overloads to allow for the templated class to behave just like the
contained type so that it can be swapped in easily. I figured the Qt
project wouldn't like the submission of the class due to its template
nature and its use of std::function but i am willing to share it if anyone
is interested.
Andre Somers
2015-04-15 16:17:01 UTC
Permalink
Post by Marc Mutz
Hi André,
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the
Q_PROPERTY
Post by André Somers
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have
encountered the
problems with which you motivate PropertyGuard.
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a
mechanism that works
on the member data directly.
Thanks,
Marc
I have actually run into the same situation and made a template class
that owns the variable. Its constructor takes an initial value and a
std::function<void (const T&)> as a callback for when the value
changes. The callback can be a lambda or a std::bind to the expected
signal. I also added overloads to allow for the templated class to
behave just like the contained type so that it can be swapped in
easily. I figured the Qt project wouldn't like the submission of the
class due to its template nature and its use of std::function but i am
willing to share it if anyone is interested.
I'd certainly be interested to seeing how you solved this, yes. Thanks!

André
Post by Marc Mutz
_______________________________________________
Development mailing list
http://lists.qt-project.org/mailman/listinfo/development
Keith Gardner
2015-04-15 17:04:13 UTC
Permalink
Post by Andre Somers
I'd certainly be interested to seeing how you solved this, yes. Thanks!
I have made a repository for the class with an example. Sorry that there
is no documentation for it. It requires C++11 support for r-value
references, std::functional, and some type traits features. In addition to
having a notify callback, it also provides an optional pre-notify callback
to let you know the current value and the value it will change to.

https://github.com/kreios4004/Property_Class
André Somers
2015-04-15 14:58:24 UTC
Permalink
Hi Marc,

Thank you for responding.
Post by Marc Mutz
Hi André,
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.
Are you sure, or is your code just not secure? ;-)

Basically, if you have this code (or something like it) somewhere, you
already have a problem:

void MyClass::setFooAndBar(QString foo, int bar)
{
if (m_foo != foo) {
m_foo = foo;
emit fooChanged(foo); //this is where things may start to go wrong
already
}

if (m_bar != bar) {
m_bar = bar;
emit barChanged(bar);
}
}

Calling separate setters instead from inside setFooAndBar doesn't help.

What happens if a slot connected to fooChanged uses the instance of
MyClass? What property values does it see? foo is changed, but bar has
not changed yet. What if that slot triggers something that ends up
deleting the instance?

You'd have to write something like this instead:

void MyClass::setFooAndBar(QString foo, int bar)
{
bool fooHasChanged(false);
bool barHasChanged(false);

if (m_foo != foo) {
m_foo = foo;
fooHasChanged = true;
}

if (m_bar != bar) {
m_bar = bar;
barHasChanged = true;
}

if (fooHasChanged) emit fooHasChanged(foo);
if (barHasChanged) emit barHasChanged(bar); // we should check if
we're still alive before we do this
}

I find my version easier to write and maintain:

void MyClass::setFooAndBar(QString foo, int bar)
{
guardProperty(foo);
guardProperty(bar);

m_foo = foo;
m_bar = bar;
}
Post by Marc Mutz
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.
I developed this still working on Qt4, and there its's all we have. Do
you have a suggestion how to get access to the metaproperty without
using the string lookup in Qt5? AFAIK, that is still string-based API.
Perhaps it could be a constexpr though, as the value really wouldn't
change at runtime (for normal C++ objects, not talking about dynamic
generated stuff here)? I doubt that will actually work, but I have not
played around with that enough yet.

Thanks for your comments though; speed is a real concern of course.

André
Matthew Woehlke
2015-04-15 15:12:57 UTC
Permalink
[Valid points about the inconsistent state of the object elided.]
What if that slot [connected to the instance property changed
signal] triggers something that ends up deleting the instance?
Then the slot is broken. What if the sender needs to do something after
it delivers the signal? What if other slots are connected? Deleting a
signal sender from a slot connected to the signal is inherently
dangerous. Don't do it.

This is why we have deleteLater()...

IMO it's perfectly valid for an object to assume that emitting a signal
will not cause it to be deleted.
--
Matthew
Christian Kandeler
2015-04-15 15:25:42 UTC
Permalink
Post by Matthew Woehlke
[Valid points about the inconsistent state of the object elided.]
What if that slot [connected to the instance property changed
signal] triggers something that ends up deleting the instance?
Then the slot is broken. What if the sender needs to do something after
it delivers the signal? What if other slots are connected? Deleting a
signal sender from a slot connected to the signal is inherently
dangerous. Don't do it.
While delete is probably the most extreme case, all other operations on
the sender have the same conceptual problem. I'd even argue that the
delete case is relatively benign, because it very likely fails loudly,
rather than in some subtle way.


Christian
Giuseppe D'Angelo
2015-04-15 17:18:15 UTC
Permalink
On 15 April 2015 at 17:12, Matthew Woehlke
Post by Matthew Woehlke
Then the slot is broken. What if the sender needs to do something after
it delivers the signal? What if other slots are connected? Deleting a
signal sender from a slot connected to the signal is inherently
dangerous. Don't do it.
For the record, there's some code in Qt to protect against this. I
believe the entire signal emission mechanism is designed to work
nonetheless (and tested), and you can probably grep for QPointer.*this
(f.i. in widgets code) to see other interesting cases. But I agree
it's damn difficult to design robust code this way.
--
Giuseppe D'Angelo
Marc Mutz
2015-04-15 19:33:12 UTC
Permalink
Post by André Somers
Basically, if you have this code (or something like it) somewhere, you
void MyClass::setFooAndBar(QString foo, int bar)
{
if (m_foo != foo) {
m_foo = foo;
emit fooChanged(foo); //this is where things may start to go wrong
already
}
if (m_bar != bar) {
m_bar = bar;
emit barChanged(bar);
}
}
1. This function has more than one responsibility. Functions with more than
one responsibility are hard to get right, to maintain, and in general
hard to make exception-safe.
2. This function doesn't have all-or-nothing / transactional semantics.

That's basically both different way of saying that functions should be
structured such that they:

1st perform anything that can fail _without_ modifying the state of the
program
2nd commit the new state with never-fail operations
3rd clean up (this includes notification)
Post by André Somers
void MyClass::setFooAndBar(QString foo, int bar)
{
bool fooHasChanged(false);
bool barHasChanged(false);
if (m_foo != foo) {
m_foo = foo;
fooHasChanged = true;
}
if (m_bar != bar) {
m_bar = bar;
barHasChanged = true;
}
if (fooHasChanged) emit fooHasChanged(foo);
if (barHasChanged) emit barHasChanged(bar); // [...]
}
This code still doesn't meet that goal. It first modifies m_foo. If the
assignment to m_bar throws, then m_foo has been changed, but m_bar hasn't.

void MyClass::setFooAndBar(QString foo, int bar)
{
const bool fooHasChanged = m_foo != foo;
const bool barHasChanged = m_bar != bar;

m_foo.swap(foo);
m_bar = bar; // int can't throw

if (fooHasChanged) emit fooHasChanged(m_foo);
if (barHasChanged) emit barHasChanged(m_bar);
}

Untested code:

#define EMIT_AT_SCOPE_EXIT_WHEN_CHANGED(value, signal) \
struct Guard##value {
decltype(value) oldValue;
decltype(value) &ref;
~Guard##value() { if (ref != oldValue) emit signal(ref); }
} guard##value = { value, value };

For C++98, the type would have to be passed instead of using decltype.
It's still less efficient than it could be, due to oldValue (imagine it being
a std::vector), but that can be fixed at the cost of more arguments. Maybe a
lot of this stuff can also be templatised, to effectively get something like:

const GuardBase &valueGuard = guard(value, &signal);

But if the call to signal in this local scope is still indirect, as it is with
most function pointers, this is still a no-no, imo.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt Experts
Matthew Woehlke
2015-04-15 15:08:11 UTC
Permalink
Post by Marc Mutz
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.
FWIW I had the same thought; also, I'm not a fan of needing the
Q_UNUSED, or using a macro to 'hide' it.

What about something like this?

QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address

It's slightly redundant because declaring the guard and adding a
property are separate, but there is no unused object, and you can use
the same guard for multiple properties.

The implementation is trickier (probably you need a dynamically
allocated templated helper class with a virtual base to store the value
and check for changes), but avoids multiple string-based look-ups.

The "slot" could be a functor, like QObject::connect accepts, that could
be any of the actual slot, std::function, lambda, etc.
--
Matthew
Andre Somers
2015-04-15 16:29:33 UTC
Permalink
Post by Matthew Woehlke
Post by Marc Mutz
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.
FWIW I had the same thought; also, I'm not a fan of needing the
Q_UNUSED, or using a macro to 'hide' it.
What about something like this?
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
It's slightly redundant because declaring the guard and adding a
property are separate, but there is no unused object, and you can use
the same guard for multiple properties.
The implementation is trickier (probably you need a dynamically
allocated templated helper class with a virtual base to store the value
and check for changes), but avoids multiple string-based look-ups.
The "slot" could be a functor, like QObject::connect accepts, that could
be any of the actual slot, std::function, lambda, etc.
I'm not sure I understand your solution, or what it yields over what I
am using now. Of course what I do is not using multiple string lookups.
The property is looked up once on construction of the guard object. Then
the current value is copied as a QVariant, as it is made available
through the property system. The signal meta-method is also stored from
the constructor, so the destructor can use it. So, only one
string-lookup needed per property. The elegance is that it uses the
property system to get the value *and* the notification signal to
trigger, so it is really easy to use.

The need for a Q_UNUSED is inherent in any RAII class that you don't
explicitly use any more. It works without, but you get a warning about
an unused variable which is annoying. QMutexLocker suffers from the same
problem.

I also don't like using a macro much, but it does yield quite an elegant
way to say what you want I think. It also hides the explicit reference
to this. You can still use the explicit way too of course, especially if
you want to access the guard object afterwards (you can cancel it and
access the original value for instance).

I am currently not seeing a need to trigger anything else than the
notification signal, but I guess that could be added if there is a need
for that.

André
Matthew Woehlke
2015-04-15 16:44:21 UTC
Permalink
Post by Andre Somers
Post by Matthew Woehlke
What about something like this?
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
It's slightly redundant because declaring the guard and adding a
property are separate, but there is no unused object, and you can use
the same guard for multiple properties.
The implementation is trickier (probably you need a dynamically
allocated templated helper class with a virtual base to store the value
and check for changes), but avoids multiple string-based look-ups.
The "slot" could be a functor, like QObject::connect accepts, that could
be any of the actual slot, std::function, lambda, etc.
I'm not sure I understand your solution, or what it yields over what I
am using now. Of course what I do is not using multiple string lookups.
The property is looked up once on construction of the guard object. Then
the current value is copied as a QVariant, as it is made available
through the property system. The signal meta-method is also stored from
the constructor, so the destructor can use it. So, only one
string-lookup needed per property. The elegance is that it uses the
property system to get the value *and* the notification signal to
trigger, so it is really easy to use.
The second form would thus save the value <-> QVariant comparison and
not much else. The third form however eliminates the string look-up
entirely by having the user provide both the backing member variable
*and* what to do when a change should be emitted. (I'd expect this to be
the change signal most of the time, but taking a functor adds the
flexibility of allowing users to do other things if needed.)
Post by Andre Somers
The need for a Q_UNUSED is inherent in any RAII class that you don't
explicitly use any more. It works without, but you get a warning about
an unused variable which is annoying. QMutexLocker suffers from the same
problem.
"Yes", but separating the construction and property binding eliminates
that (because you "used" the guard after all) and allows one guard to
track multiple properties, with a potential saving in memory complexity.
The down side is that it's a little more typing, so it's a trade-off.
--
Matthew
Andre Somers
2015-04-15 17:11:09 UTC
Permalink
Post by Matthew Woehlke
The second form would thus save the value <-> QVariant comparison and
not much else. The third form however eliminates the string look-up
entirely by having the user provide both the backing member variable
*and* what to do when a change should be emitted. (I'd expect this to
be the change signal most of the time, but taking a functor adds the
flexibility of allowing users to do other things if needed.)
Right, of course. That may be useful indeed. Thank you for your
feedback, it is apreciated!

André
Alan Alpert
2015-04-15 17:55:12 UTC
Permalink
On Wed, Apr 15, 2015 at 8:08 AM, Matthew Woehlke
Post by Matthew Woehlke
Post by Marc Mutz
Post by André Somers
void MyClass::setFoo(QString value)
{
PropertyGuard guard(this, "foo"); //foo is the name of the Q_PROPERTY
Q_UNUSED(guard);
m_foo = value;
}
This is an interesting idea, though I don't think I have encountered the
problems with which you motivate PropertyGuard.
I have, it comes up a lot in objects used as an interface to QML
(where every fooChanged signal will probably trigger binding
re-evaluation or JS blocks). I don't think it's that hard to manage,
but PropertyGuard does look easier and saves some boilerplate code.
Post by Matthew Woehlke
Post by Marc Mutz
For use in a library, though, I fear the string-based mechanism is too
inefficient. For use within QtWidgets, say, I'd suggest a mechanism that works
on the member data directly.
I think it's fine to tie it to the property system, since conceptually
it needs a both a read and a notify on the datum.

For performance, allow passing in a metaproperty index instead of a
property name string. It will still invoke the getter, but they're
rarely that complicated (and when they are, you need to go off the
getter value*).
Post by Matthew Woehlke
FWIW I had the same thought; also, I'm not a fan of needing the
Q_UNUSED, or using a macro to 'hide' it.
What about something like this?
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
It's slightly redundant because declaring the guard and adding a
property are separate, but there is no unused object, and you can use
the same guard for multiple properties.
The common case is one property I think, so keep that case to one
line. I'd envision using it in all my basic setters to save code at
the start of a project, and then when the features start to creep in
it's easier to add complexity into the setters.

Still, you could always leave in a convenience constructor or just
wrap it in a macro as before.

* Example of when you need to go off getter value instead of member is
usually sometime like implicit width, where the getter looks like
getWidth() {
if (m_width == -1)
return m_implicitWidth;
return m_width;
}
In this case, if you have m_width = 80, m_implicitWidth = 80, and call
setWidth(-1), then you actually don't want to emit widthChanged even
though m_width is updated.

That was just an example, not how implicit width actually is
implemented in QtQuick.

--
Alan Alpert
Matthew Woehlke
2015-04-15 18:23:12 UTC
Permalink
Post by Alan Alpert
The common case is one property I think, so keep that case to one
line. I'd envision using it in all my basic setters to save code at
the start of a project, and then when the features start to creep in
it's easier to add complexity into the setters.
If you use the helper class always, sure. I was thinking I probably
would not use it for the "easy" cases, i.e. where only one value
changes, and instead use it only where it becomes more important to have
the helper, in which case it's more likely you have two or more values.
Anyway...
Post by Alan Alpert
Still, you could always leave in a convenience constructor or just
wrap it in a macro as before.
...there's always this :-). The case with one property (or, for that
matter, if you don't care about having multiple instances of the helper)
can still use a macro to collapse the two lines of code that either
version "needs".

For that matter, the addProperty flavor doesn't preclude having
convenience ctors that also call addProperty :-).
--
Matthew
Marc Mutz
2015-04-15 19:03:04 UTC
Permalink
Post by Matthew Woehlke
FWIW I had the same thought; also, I'm not a fan of needing the
Q_UNUSED, or using a macro to 'hide' it.
I haven't had to use Q_UNUSED on a RAII object since iirc GCC 3 times. What
broken compilers are you guys using?
Post by Matthew Woehlke
What about something like this?
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
There's type erasure going on, so it will allocate memory. Allocating memory
in a setter that might just change an int is what I call inefficient.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt Experts
Keith Gardner
2015-04-15 19:05:39 UTC
Permalink
Post by Marc Mutz
Post by Matthew Woehlke
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
There's type erasure going on, so it will allocate memory. Allocating memory
in a setter that might just change an int is what I call inefficient.
Would something like this be better?

// constructor
// Provide an initial value and a std::function to notify about the change
Property<bool> m_example = Property<bool>(false, [&](bool value){
emit exampleChanged(value);
});

// getter
bool *::example() const {
return m_example;
}

// setter
void *::setExample(bool value) {
m_example = value;
}

The working code for this example can be found
https://github.com/kreios4004/Property_Class.
Andre Somers
2015-04-15 19:19:31 UTC
Permalink
Post by Marc Mutz
Post by Matthew Woehlke
QPropertyGuard g{this};
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
There's type erasure going on, so it will allocate memory.
Allocating memory
in a setter that might just change an int is what I call inefficient.
Would something like this be better?
// constructor
// Provide an initial value and a std::function to notify about the change
Property<bool> m_example = Property<bool>(false, [&](bool value){
emit exampleChanged(value);
});
// getter
bool *::example() const {
return m_example;
}
// setter
void *::setExample(bool value) {
m_example = value;
}
The working code for this example can be found
https://github.com/kreios4004/Property_Class.
Thank you for posting that.

I took a look at it, but I am not sure it actually solves much. It moves
the check if something changed to the wrapped variable, but it makes
using that value then more complicated. Instead of using the variable
directly, one now has to go through m_example.getValue() everywhere. Not
pretty, I think. The case of modifying more than one property in one go
is also not solved. On the one hand, dependent properties (such a
isValid property) are not updated unless you manually write that code
again, and on the other hand if you use this to set more than one
property in a method, you still are sending signals at the point the
class is in an inconsistent state.

One nice side effect of my implementation is that the notification is
actually only send once. Even if the property value is changed multiple
times in the meantime, you still get only one notification. That can be
very convenient, even if you don't use it much.

André
Keith Gardner
2015-04-15 19:29:02 UTC
Permalink
Post by Keith Gardner
Post by Matthew Woehlke
QPropertyGuard g{this};
Post by Matthew Woehlke
g.addProperty("a"); // use QObject::property
g.addProperty("b", m_b); // take value member by reference
g.addProperty(m_c, &cChanged); // ...and also slot address
There's type erasure going on, so it will allocate memory. Allocating memory
in a setter that might just change an int is what I call inefficient.
Would something like this be better?
// constructor
// Provide an initial value and a std::function to notify about the change
Property<bool> m_example = Property<bool>(false, [&](bool value){
emit exampleChanged(value);
});
// getter
bool *::example() const {
return m_example;
}
// setter
void *::setExample(bool value) {
m_example = value;
}
The working code for this example can be found
https://github.com/kreios4004/Property_Class.
Thank you for posting that.
I took a look at it, but I am not sure it actually solves much. It moves
the check if something changed to the wrapped variable, but it makes using
that value then more complicated. Instead of using the variable directly,
one now has to go through m_example.getValue() everywhere.
This is partially true. If it is containing a class or a struct, then you
are correct. If it is an int or float and you want to perform some math
operation with it, it has the operator const T&() to perform the get
automatically.
Post by Keith Gardner
Not pretty, I think. The case of modifying more than one property in one
go is also not solved. On the one hand, dependent properties (such a
isValid property) are not updated unless you manually write that code
again, and on the other hand if you use this to set more than one property
in a method, you still are sending signals at the point the class is in an
inconsistent state.
True. My goal was to perform a type safe way of change detection so I
wouldn't have to write the boilerplate code over and over. It also
provides a way customize the way the signal is called in the callback. It
does suffer in the more complex scenarios when more than one operation is
happening to the object.
Post by Keith Gardner
One nice side effect of my implementation is that the notification is
actually only send once. Even if the property value is changed multiple
times in the meantime, you still get only one notification. That can be
very convenient, even if you don't use it much.
This is very appealing.
Marc Mutz
2015-04-15 19:34:57 UTC
Permalink
Post by Keith Gardner
Would something like this be better?
std::function
No, a std::function in general allocates memory and even if it uses the small
object optimisation, the call to the lambda will still be indirect.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt Experts
Continue reading on narkive:
Loading...