Re: [Development] RFC: RAII for property changes
Hi, On Wed, Apr 15, 2015 at 11:49 AM, André Somers an...@familiesomers.nl wrote: 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 2015-04-15 10:43, Marc Mutz wrote: On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
Hi Marc, Thank you for responding. Marc Mutz schreef op 15-4-2015 om 16:43: Hi André, On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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; } 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é ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 04/15/2015 05:12 PM, Matthew Woehlke wrote: [Valid points about the inconsistent state of the object elided.] On 2015-04-15 10:58, André Somers wrote: 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
[Valid points about the inconsistent state of the object elided.] On 2015-04-15 10:58, André Somers wrote: 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wed, Apr 15, 2015 at 9:38 AM Marc Mutz marc.m...@kdab.com wrote: Hi André, On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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::functionvoid (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. ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
Hi André, On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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 marc.m...@kdab.com | Senior Software Engineer KDAB (Deutschland) GmbH Co.KG, a KDAB Group Company Tel: +49-30-521325470 KDAB - The Qt Experts ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 15 April 2015 at 17:12, Matthew Woehlke mw_tr...@users.sourceforge.net wrote: 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 15-4-2015 16:54, Keith Gardner wrote: On Wed, Apr 15, 2015 at 9:38 AM Marc Mutz marc.m...@kdab.com mailto:marc.m...@kdab.com wrote: Hi André, On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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::functionvoid (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é ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 15-4-2015 17:08, Matthew Woehlke wrote: On 2015-04-15 10:43, Marc Mutz wrote: On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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é ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 2015-04-15 12:29, Andre Somers wrote: On 15-4-2015 17:08, Matthew Woehlke wrote: 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.) 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 15-4-2015 18:44, Matthew Woehlke wrote: 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é ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wed, Apr 15, 2015 at 8:08 AM, Matthew Woehlke mw_tr...@users.sourceforge.net wrote: On 2015-04-15 10:43, Marc Mutz wrote: On Wednesday 15 April 2015 11:49:56 André Somers wrote: 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. 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*). 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 2015-04-15 13:55, Alan Alpert wrote: 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... 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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wed, Apr 15, 2015 at 2:20 PM Andre Somers an...@familiesomers.nl wrote: On 15-4-2015 21:05, Keith Gardner wrote: 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 Propertybool m_example = Propertybool(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. 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. 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. ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wednesday 15 April 2015 16:58:24 André Somers wrote: 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); } } 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) 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 marc.m...@kdab.com | Senior Software Engineer KDAB (Deutschland) GmbH Co.KG, a KDAB Group Company Tel: +49-30-521325470 KDAB - The Qt Experts ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wednesday 15 April 2015 17:08:11 Matthew Woehlke wrote: 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? 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 marc.m...@kdab.com | Senior Software Engineer KDAB (Deutschland) GmbH Co.KG, a KDAB Group Company Tel: +49-30-521325470 KDAB - The Qt Experts ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
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 Propertybool m_example = Propertybool(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. ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On 15-4-2015 21:05, Keith Gardner wrote: 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 Propertybool m_example = Propertybool(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é ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
Re: [Development] RFC: RAII for property changes
On Wednesday 15 April 2015 21:05:39 Keith Gardner wrote: 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 marc.m...@kdab.com | Senior Software Engineer KDAB (Deutschland) GmbH Co.KG, a KDAB Group Company Tel: +49-30-521325470 KDAB - The Qt Experts ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development
[Development] RFC: RAII for property changes
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 ___ Development mailing list Development@qt-project.org http://lists.qt-project.org/mailman/listinfo/development