C++ QProperty binding Question
-
Following is the code snippet. Code is working properly. No issue with code. I have understanding issue. How this internally works.
QProperty<int> height { 50}; QProperty<int> width { 100}; QProperty<int> area; area.setBinding([&](){ qDebug() << " This is called one more time" << Qt::endl; return width * height; }); width = 4; height = 40; width = 10;How the change in width or height triggers the re-evaluate of area ?
Area is binded to lambda expression. Inside the lambda we have reference to width & height. How the notify(change) signal of width or height is bound to area ? Is there any parsing happens for lambda expression, finds the width or height & then it does the some thing ?
Looking for an answer on binding is built internally. -
cough cough My voice is not my own:
The setBinding function itself doesn't directly analyze the lambda body or "know" about the variables width and height. Instead, the QProperty class provides a mechanism for registering dependencies between properties.
When you call setBinding, you're essentially telling the QProperty object that the value of this property (area in your case) depends on the values of other properties, namely width and height.
The lambda expression you provide is a function that calculates the value of the area property based on the values of width and height. However, it's not the lambda expression itself that the QProperty object "understands" or keeps track of; it's the fact that you've registered a dependency on width and height.
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property. So, whenever the values of these dependencies change (i.e., when width or height changes), the QProperty object knows that it needs to re-evaluate the binding function associated with the property.
In essence, the QProperty class provides a mechanism for tracking dependencies between properties based on the information you provide when you set up bindings. It doesn't directly analyze the lambda body or "understand" the variables used inside it; it simply keeps track of the dependencies you specify.
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
No.
The QProperty knows its dependency because when you call setBinding with a lambda, the lambda is executed and it tracks the other properties that are being evaluated in it.
If both
widthandheightareQProperty<int>.
width * heightis first calling the implicitQProperty<int>::operator int()for both, converting them tointso it can call the arithmetic operator * on their values. This operator int() is what is registering the dependency.To put it otherwise, when calling
area.setBinding([&](){ return width * height; });here is what happens:- the lambda is stored in the area QProperty to be called later.
areais marked as the current property being evaluated.- the lambda is then executed.
widthis being accessed through its conversion operator, it is thus being registered as a dependency of the currently evaluated property (areahere)- the same happens for
height
- the return value of the lambda is assigned to
area areais no longer marked as the currently evaluated property.
After that, every time that
widthandheightare changed, the lambda is being reevaluated and the above process redone. -
Following is the code snippet. Code is working properly. No issue with code. I have understanding issue. How this internally works.
QProperty<int> height { 50}; QProperty<int> width { 100}; QProperty<int> area; area.setBinding([&](){ qDebug() << " This is called one more time" << Qt::endl; return width * height; }); width = 4; height = 40; width = 10;How the change in width or height triggers the re-evaluate of area ?
Area is binded to lambda expression. Inside the lambda we have reference to width & height. How the notify(change) signal of width or height is bound to area ? Is there any parsing happens for lambda expression, finds the width or height & then it does the some thing ?
Looking for an answer on binding is built internally.@dheerendra
Just a quick answer from me, I don't use QML so you may get a better answer from someone who does, I don't want to take up too much space.https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html does say:
When a property's dependencies change in value, the property is automatically updated according to the specified relationship.
Behind the scenes, the QML engine monitors the property's dependencies (that is, the variables in the binding expression). When a change is detected, the QML engine re-evaluates the binding expression and applies the new result to the property.
And the example in https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html#using-keyword-this-keyword-with-property-binding does say
To bind the Rectangle's height to its own width, the binding expression must explicitly refer to this.width (or alternatively, rect.width):
This does imply to me that QML builds a "graph" for a binding which specifies each base property it references so it knows when to recalculate.
-
@dheerendra
Just a quick answer from me, I don't use QML so you may get a better answer from someone who does, I don't want to take up too much space.https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html does say:
When a property's dependencies change in value, the property is automatically updated according to the specified relationship.
Behind the scenes, the QML engine monitors the property's dependencies (that is, the variables in the binding expression). When a change is detected, the QML engine re-evaluates the binding expression and applies the new result to the property.
And the example in https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html#using-keyword-this-keyword-with-property-binding does say
To bind the Rectangle's height to its own width, the binding expression must explicitly refer to this.width (or alternatively, rect.width):
This does imply to me that QML builds a "graph" for a binding which specifies each base property it references so it knows when to recalculate.
-
@J-Hilk said in C++ QProperty binding Question:
this is c++ propertybindings, nothing QML related here
Oh, damn, I should not have jumped in then, sorry! I assumed coz it was in QML and Qt Quick....
-
@dheerendra
Just a quick answer from me, I don't use QML so you may get a better answer from someone who does, I don't want to take up too much space.https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html does say:
When a property's dependencies change in value, the property is automatically updated according to the specified relationship.
Behind the scenes, the QML engine monitors the property's dependencies (that is, the variables in the binding expression). When a change is detected, the QML engine re-evaluates the binding expression and applies the new result to the property.
And the example in https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html#using-keyword-this-keyword-with-property-binding does say
To bind the Rectangle's height to its own width, the binding expression must explicitly refer to this.width (or alternatively, rect.width):
This does imply to me that QML builds a "graph" for a binding which specifies each base property it references so it knows when to recalculate.
@JonB
Thanks Jon. QML I have clarity.This is C++ code binding introduced in Qt 6+. Property binding in C++. This lamda expression confused me. I can write any code inside the lambda. I can refer the variables in side. Do something else etc. So how binding is built with variable which are referred in side the lambda.
-
@JonB
Thanks Jon. QML I have clarity.This is C++ code binding introduced in Qt 6+. Property binding in C++. This lamda expression confused me. I can write any code inside the lambda. I can refer the variables in side. Do something else etc. So how binding is built with variable which are referred in side the lambda.
@dheerendra
Well, I've jumped in here erroneously already so why not stick my head out further?Formulating a Property Binding still talks about
Any C++ expression evaluating to the correct type can be used as a binding expression and be given to the setBinding() method. However, to formulate a correct binding, some rules must be followed.
Dependency tracking only works on bindable properties. It's the developer's responsibility to ensure that all properties used in the binding expression are bindable properties. When non-bindable properties are used in a binding expression, changes to those properties do not trigger updates to the bound property. No warning or error is generated either at compile-time or at run-time. The bound property will be updated only when bindable properties used in the binding expression are changed
So we are still talking about "tracking bindable properties in an expression" to decide whether the expression's value has changed. Per your template <typename Functor> QPropertyBinding<T> QProperty::setBinding(Functor f)
The property's value is set to the result of evaluating the new binding. Whenever a dependency of the binding changes, the binding will be re-evaluated, and the property's value gets updated accordingly.
I think the code really does see that you are accessing other properties and so update. But i really will shut up now, as you probably guessed this too!
OK, finally I think this really does say it: under Qt Bindable Properties
Qt provides bindable properties. Bindable properties are properties which either have a value or are specified using any C++ function, typically a C++ lambda expression. In case they are specified using a C++ function, they are updated automatically whenever their dependencies change.
-
@JonB
Thanks Jon. QML I have clarity.This is C++ code binding introduced in Qt 6+. Property binding in C++. This lamda expression confused me. I can write any code inside the lambda. I can refer the variables in side. Do something else etc. So how binding is built with variable which are referred in side the lambda.
cough cough My voice is not my own:
The setBinding function itself doesn't directly analyze the lambda body or "know" about the variables width and height. Instead, the QProperty class provides a mechanism for registering dependencies between properties.
When you call setBinding, you're essentially telling the QProperty object that the value of this property (area in your case) depends on the values of other properties, namely width and height.
The lambda expression you provide is a function that calculates the value of the area property based on the values of width and height. However, it's not the lambda expression itself that the QProperty object "understands" or keeps track of; it's the fact that you've registered a dependency on width and height.
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property. So, whenever the values of these dependencies change (i.e., when width or height changes), the QProperty object knows that it needs to re-evaluate the binding function associated with the property.
In essence, the QProperty class provides a mechanism for tracking dependencies between properties based on the information you provide when you set up bindings. It doesn't directly analyze the lambda body or "understand" the variables used inside it; it simply keeps track of the dependencies you specify.
-
cough cough My voice is not my own:
The setBinding function itself doesn't directly analyze the lambda body or "know" about the variables width and height. Instead, the QProperty class provides a mechanism for registering dependencies between properties.
When you call setBinding, you're essentially telling the QProperty object that the value of this property (area in your case) depends on the values of other properties, namely width and height.
The lambda expression you provide is a function that calculates the value of the area property based on the values of width and height. However, it's not the lambda expression itself that the QProperty object "understands" or keeps track of; it's the fact that you've registered a dependency on width and height.
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property. So, whenever the values of these dependencies change (i.e., when width or height changes), the QProperty object knows that it needs to re-evaluate the binding function associated with the property.
In essence, the QProperty class provides a mechanism for tracking dependencies between properties based on the information you provide when you set up bindings. It doesn't directly analyze the lambda body or "understand" the variables used inside it; it simply keeps track of the dependencies you specify.
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property.
Exactly! But how does it have access to do that? The C++ lambda is compiled into code by the time it gets passed to
setBinding(), is it not, so what calls are available to say "what variables/properties are referenced in this compiled C++ expression/functor"? I guess "reflection" somehow, but how does it even see thatwidthorheightare in the expression once it's compiled?P.S.
Are these "Qt Bindable Properties" new at Qt6, so I (Qt5) cannot even play with them? -
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property.
Exactly! But how does it have access to do that? The C++ lambda is compiled into code by the time it gets passed to
setBinding(), is it not, so what calls are available to say "what variables/properties are referenced in this compiled C++ expression/functor"? I guess "reflection" somehow, but how does it even see thatwidthorheightare in the expression once it's compiled?P.S.
Are these "Qt Bindable Properties" new at Qt6, so I (Qt5) cannot even play with them?@JonB @J-Hilk
Thank you. Yes QProperty is doing this. Unless some scanning of lambda happens it cannot achieve this. When does this happen ? How does it happen ? Some code scanning must happen. Is there some preprocessing like Moc. I know moc is not doing.
Any way code works. I have imagined like oh something happens like this. However when what how etc were not clear.
-
@JonB @J-Hilk
Thank you. Yes QProperty is doing this. Unless some scanning of lambda happens it cannot achieve this. When does this happen ? How does it happen ? Some code scanning must happen. Is there some preprocessing like Moc. I know moc is not doing.
Any way code works. I have imagined like oh something happens like this. However when what how etc were not clear.
@dheerendra Qt is open source you can look in the code to see how this overload of setBinding works
-
Following is the code snippet. Code is working properly. No issue with code. I have understanding issue. How this internally works.
QProperty<int> height { 50}; QProperty<int> width { 100}; QProperty<int> area; area.setBinding([&](){ qDebug() << " This is called one more time" << Qt::endl; return width * height; }); width = 4; height = 40; width = 10;How the change in width or height triggers the re-evaluate of area ?
Area is binded to lambda expression. Inside the lambda we have reference to width & height. How the notify(change) signal of width or height is bound to area ? Is there any parsing happens for lambda expression, finds the width or height & then it does the some thing ?
Looking for an answer on binding is built internally.@dheerendra
May I suggest you try this.- To your properties add
QProperty<int> dummy { 42 }; - To your lambda add
qDebug() << "area being recalculated";.
Now try altering the value of
dummy, only. Does theqDebug()get called?-
If it does then re-evaluation is not tied to the fact that the lambda only accesses
width&height, it looks like a change to any property triggers re-evaluation. I'm kind of hoping it works this way, bad for efficiency, good for understanding! -
If it does not get called then somehow Qt has figured out that the lambda accesses only certain variables/properties, and I'm not sure how it dos that from code.
- To your properties add
-
cough cough My voice is not my own:
The setBinding function itself doesn't directly analyze the lambda body or "know" about the variables width and height. Instead, the QProperty class provides a mechanism for registering dependencies between properties.
When you call setBinding, you're essentially telling the QProperty object that the value of this property (area in your case) depends on the values of other properties, namely width and height.
The lambda expression you provide is a function that calculates the value of the area property based on the values of width and height. However, it's not the lambda expression itself that the QProperty object "understands" or keeps track of; it's the fact that you've registered a dependency on width and height.
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
Once the QProperty object identifies these external variables, it stores them as dependencies for the property. So, whenever the values of these dependencies change (i.e., when width or height changes), the QProperty object knows that it needs to re-evaluate the binding function associated with the property.
In essence, the QProperty class provides a mechanism for tracking dependencies between properties based on the information you provide when you set up bindings. It doesn't directly analyze the lambda body or "understand" the variables used inside it; it simply keeps track of the dependencies you specify.
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
No.
The QProperty knows its dependency because when you call setBinding with a lambda, the lambda is executed and it tracks the other properties that are being evaluated in it.
If both
widthandheightareQProperty<int>.
width * heightis first calling the implicitQProperty<int>::operator int()for both, converting them tointso it can call the arithmetic operator * on their values. This operator int() is what is registering the dependency.To put it otherwise, when calling
area.setBinding([&](){ return width * height; });here is what happens:- the lambda is stored in the area QProperty to be called later.
areais marked as the current property being evaluated.- the lambda is then executed.
widthis being accessed through its conversion operator, it is thus being registered as a dependency of the currently evaluated property (areahere)- the same happens for
height
- the return value of the lambda is assigned to
area areais no longer marked as the currently evaluated property.
After that, every time that
widthandheightare changed, the lambda is being reevaluated and the above process redone. -
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
No.
The QProperty knows its dependency because when you call setBinding with a lambda, the lambda is executed and it tracks the other properties that are being evaluated in it.
If both
widthandheightareQProperty<int>.
width * heightis first calling the implicitQProperty<int>::operator int()for both, converting them tointso it can call the arithmetic operator * on their values. This operator int() is what is registering the dependency.To put it otherwise, when calling
area.setBinding([&](){ return width * height; });here is what happens:- the lambda is stored in the area QProperty to be called later.
areais marked as the current property being evaluated.- the lambda is then executed.
widthis being accessed through its conversion operator, it is thus being registered as a dependency of the currently evaluated property (areahere)- the same happens for
height
- the return value of the lambda is assigned to
area areais no longer marked as the currently evaluated property.
After that, every time that
widthandheightare changed, the lambda is being reevaluated and the above process redone.@GrecKo said in C++ QProperty binding Question:
width * height is first calling the implicit QProperty<int>::operator int() for both, converting them to int so it can call the arithmetic operator * on their values. This operator
int()is what is registering the dependency.[My italics.] Wow! OK, that answers the question as to how it gets at the dependencies without "analyzing" code, which it couldn't really do from C++. But that means there must be this or an equivalent for every possible operation you could do on a
QProperty. Makes you wonder if you find some expression involving aQPropertywhich cannot be "tracked" like this, but I guess not. -
@J-Hilk said in C++ QProperty binding Question:
Internally, when you call setBinding, the QProperty object can inspect the lambda expression and identify any external variables that are captured by reference. In your lambda & { return width * height; }, width and height are captured by reference (&), indicating that they are external variables used inside the lambda body.
No.
The QProperty knows its dependency because when you call setBinding with a lambda, the lambda is executed and it tracks the other properties that are being evaluated in it.
If both
widthandheightareQProperty<int>.
width * heightis first calling the implicitQProperty<int>::operator int()for both, converting them tointso it can call the arithmetic operator * on their values. This operator int() is what is registering the dependency.To put it otherwise, when calling
area.setBinding([&](){ return width * height; });here is what happens:- the lambda is stored in the area QProperty to be called later.
areais marked as the current property being evaluated.- the lambda is then executed.
widthis being accessed through its conversion operator, it is thus being registered as a dependency of the currently evaluated property (areahere)- the same happens for
height
- the return value of the lambda is assigned to
area areais no longer marked as the currently evaluated property.
After that, every time that
widthandheightare changed, the lambda is being reevaluated and the above process redone.@GrecKo said in C++ QProperty binding Question:
the lambda is then executed.
Thanks GrecKo. This clears my thought process. Lambda is getting executed the moment you do the setBinding.
-
D dheerendra has marked this topic as solved on