How are QML property dependencies determined? (and how to manipulate them) - qt

A property that is bound to an expression is updated when something in the expression changes.
This is called a dependency.
EDIT:
To clarify:
I'm interested in details on how Qt determines a list of dependencies
Dependencies on simple bindings such as x: y are more or less obvious
The question is about less obvious cases such as x: myItemId["y"] and x: myFunction(z) where myFunction(p) { if (p) return myItemId.y }
Sometimes QML engine is able to detect change even if the expression is a function call without arguments, other times it cannot do that (for example mapToItem(item,0,0).x).
Another example of imperfection is that setting JS array item value without reassigning the array itself doesn't normally produce onXxxxxChanged signal or update anything referring to that array value.
An expression with unused result (x: {myForcedDependency; return myActualCalculation()}) is sometimes suggested to force a dependency.
According to this KDAB article and Qt source code, a binding expression is not only evaluated but any properties "accessed" during that are "captured" in something called a "guard", then every guard properties onXxxxxChanged() signals are connected, but actual details of this process are unclear.
So my questions are:
Are there any defined rules of dependency resolution?
How does it really work?
How deeply does QQmlEngine/V8 scan "accesses" into functions called by the binding expression and what may prevent it from doing that?
Is dependency-detection only based on the first attempt at property resolution?
Are all possible code paths checked even if execution never reached there yet?
Are non-trivial accesses determined in those cases, such as object["property"] syntax?
What if some unexecuted code is (currently) erroneous (and does not produce an error but cannot be properly analyzed)?
How can the dependency resolution process be influenced?
Is there a way to avoid or block a dependency?
As far as I understand an intermediate "filter" property that only actually changes its value when it's necessary to update is the intended way, correct?
Is there an intended way to force a dependency?
Is manually emitting "XxxxxChanged" signal the correct/supported way to force an update?
Is adding an unused reference a legal/intended way to do it or undefined behavior based on the current implementation quirk?
Any information would be useful, although I did read the official documentation on QML properties, QML bindings and JavaScript expressions and didn't find any concrete explanation - if you refer to the official documentation please quote relevant parts.
Please note that I'm not asking you to test if any of this works on your system, but if it's supposed to work - if it can be relied on

It makes more sense if you just think of bindings as connected signals. If you have something like this:
property int x: y
It's just like doing this in C++:
connect(this, &SomeClass::yChanged, [this]() { x = y; });
The same goes for expressions:
property int x: y + z
would be equivalent to:
connect(this, &SomeClass::yChanged, [this]() { x = y + z; });
connect(this, &SomeClass::zChanged, [this]() { x = y + z; });
And the same with function calls:
property int x: someFunc()
function someFunc() {
return y;
}
The only time bindings don't update is when there is no onChanged signal to connect to, or the onChanged signal doesn't get emitted for whatever reason.
property int x: cppObject.invokable()
In the above case, the only property that x is able to connect to is cppObject. If invokable references other properties, those won't be connected to x and therefore the binding won't update.
property var array: [1, 2, 3]
property int x: array[0]
function updateArray() {
array = [2, 4, 6]
arrayChanged() // Manually call the onChanged signal to update `x`
}
var properties do not notify by default (for some reason). So in this case, we have to manually call the changed signal, but then the binding will still work.

For a property var, onChanged is emitted only when there is a direct assignment to the var itself, not to a property of some object it refers to. This also excludes modification of array contents, as JS arrays are JS objects.
This is consistent with QML being a JS extension. In JS you can modify prop in this code, because const only means variable will always refer to the same object:
const variable = { prop: 'value' };
Just like only direct assignments to const variables are regarded as change attempts when JS enforces const, QML only emits onChanged on direct assignments to a property var.
Coming from C++, I like to compare JS variables with object value to pointers:
SomeClass *variable = new SomeClass();
SomeClass *const variable = new SomeClass(); //const pointer to mutable object
Again, a change in the referred object is not regarded as a change in the variable.

Related

Does CFBridgingRelease restore ownership to preexisting references without direct assignment?

If I have the following code:
// objective C++ code .mm
id<MTLTexture> texture = ...;
void* ptr = (void*)CFBridgingRetain(texture);
share_ptr_with_native_code(ptr);
[texture do_stuff]; // is this valid?
// native code .cpp
void share_ptr_with_native(void* ptr)
{
ptr->do_stuff();
CFBridgingRelease(ptr);
}
Will texture be valid and retained by ARC again after the call to share_ptr_with_native()?
Other than various errors in your code snippet, yes, the line in question is valid. ARC continues to maintain its own strong reference to object while it's still in use in the top code, in addition to the one that you become responsible for. CFBridgingRetain() has a +1 effect on the retain count of the object, hence "retain" in its name.
Even everything said is right, it would be nicer if you change your
CFBridgingRelease(ptr);
to
CFRelease(ptr) .
__bridge_retained or CFBridgingRetain casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to you.
You are responsible for calling CFRelease or a related function to relinquish ownership of the object.
Taken from https://developer.apple.com/library/content/documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html.

glVertexAttribPointer last attribute value or pointer

The last attribute of glVertexAttribPointer is of type const GLvoid*. But is it really a pointer? It is actually an offset. If I put 0, it means an offset of 0 and not a null pointer to an offset. In my engine, I use this function:
void AbstractVertexData::vertexAttribPtr(int layout) const
{
glVertexAttribPointer(layout,
getShaderAttribs()[layout]->nbComponents,
static_cast<GLenum>(getShaderAttribs()[layout]->attribDataType),
getShaderAttribs()[layout]->shouldNormalize,
getVertexStride(layout),
reinterpret_cast<const void*>(getVertexAttribStart(layout)));
}
getVertexAttribStart returns an intptr_t. When I run drmemory, it says "uninitialized read" and I want to remove that warning. This warning comes from the reinterpret_cast. I can't static_cast to a const void* since my value isn't a pointer. What should I do to fix this warning?
Originally, back in OpenGL-1.1 when vertex arrays got introduces, functions like glVertexPointer, glTexCoordPointer and so on were accepting pointers into client address space. When shaders got introduced they came with arbitrary vertex attributes and the function glVertexAttribPointer follows the same semantics (this was in OpenGL-2.1).
The buffer objects API was then reusing existing functions, where you'd pass an integer for a pointer parameter.
OpenGL-3.3 core eventually made the use of buffer objects mandatory and ever since the glVertexAttribPointer functions being defines with a void* in their function signature are a sore spot; I've written in extent about it in https://stackoverflow.com/a/8284829/524368 (but make sure to read the rest of the answers as well).
Eventually new functions got introduced that allow for a more fine grained control over how vertex attributes are accessed, replacing glVertexAttribPointer, and those operate purely on offsets.

What's the Nicest Way to do This? (Qt and Enum style arguments)

In Qt, it is common to see something similar to the following:
QSettings obj3(QSettings::SystemScope, "MySoft", "Star Runner");
The important bit is the QSettings::SystemScope, which is an enum.
I want to have a settings provider (pay no attention to the previous example here, it has nothing to do with the following), with a get/set property.
Settings.set(Settings::refreshRate)
The refreshRate has to link to a key (string), and a default value (variant).
Should I make an enum and two dicts for the key and default values, or make a struct and a whole bunch of variables that encapsulate the settings I need? Should I try something else?
Thanks!
Edit!
This is what I did.
// Interface
class Settings {
public:
static QVariant get(Setting setting);
static void set(Setting setting, QVariant value);
const static Setting serverRefreshRate;
const static Setting serverReportTimeout;
};
// Implementation
const Setting Settings::serverRefreshRate = { "server/refreshRate", 10000 };
const Setting Settings::serverReportTimeout = { "server/reportTimeout", 1000 };
Well I guess since you're using enum which most likely will be easily castable to numbers from to 0 to N-1 I guess just storing variants and strings in two vectors or one vector of pairs would work just fine.
There's also another question though -- how to initialize all of that and how you will be adding new settings to it. I can suggest two methods - first one writing a bunch of function calls with arguments: enum, string, variant. Thus way though if programmer adds another value to enum he can forget to call initializing function. The other way is to create function (or maybe two) which will do switch on all enum values (without default case) and will return pair of string and variant. You can turn on the compiler warning about all enum values being processed in switch and thus way control if you forget to implement some of them in that function. And then initialize your structures using loop on all of enum values. These initializing functions should be called somewhere near the beginning of your program (before reading settings initially).
Well, that's my thoughts on it, you are free to try some different ways though.

Do I need to use a weak pointer when using C++ `function` blocks (as opposed to Objective C blocks)

If you capture a strong reference to self under ARC in an objective-C style block, you need to use a __weak pointer to avoid an ARC "retain cycle" problem.
// Right way:
- (void)configureBlock {
XYZBlockKeeper * __weak weakSelf = self;
self.block = ^{
[weakSelf doSomething]; // capture the weak reference
// to avoid the reference cycle
}
}
I really don't know what a retain cycle is, but this answer describes it a bit. I just know you should use a __weak pointer for Objective-C style blocks. See Avoid Strong Reference Cycles when Capturing self.
But my question is, do I need to create a weak pointer when capturing self under a C++ <functional> block?
- (void)configureBlock {
self.block = [self](){
[self doSomething]; // is this ok? It's not an objective C block.
}
}
C++ lambdas can captured variables either by value or by reference (you choose when you declare the lambda how to capture each variable).
Capturing by reference is not interesting, because references to local variables become invalid after you leave the variable's scope anyway, so there is no memory management issues at all.
Capturing by value: if the captured variable is an Objective-C object pointer type, then it gets interesting. If you are using MRC, nothing happens. If you are using ARC, then yes, the lambda "retains" captured variables of object pointer type, as long as they are __strong (not __weak or __unsafe_unretained). So, yes, it would create a retain cycle.

Modifying a QVariantMap with JavaScript

The QtWebKit Bridge documentation states the following -
Compound (JSON) objects JavaScript
compound objects, also known as JSON
objects, are variables that hold a
list of key-value pairs, where all the
keys are strings and the values can
have any type. This translates very
well to QVariantMap, which is nothing
more than a QMap of QString to
QVariant. The seamless conversion
between JSON objects and QVariantMap
allows for a very convenient way of
passing arbitrary structured data
between C++ and the JavaScript
environment. The native QObject has to
make sure that compound values are
converted to QVariantMaps and
QVariantLists, and JavaScript is
guaranteed to receive them in a
meaningful way. Note that types that
are not supported by JSON, such as
JavaScript functions and
getters/setters, are not converted.
Does this mean that, while JavaScript is able to read a QVariantList, it is unable to modify it?
I've tried adding a getter and setter for test purposes -
Q_PROPERTY( QVariantMap Settings READ GetShadowSettings WRITE SetShadowSettings )
The getter function is being called when the JavaScript wants to access any data from the QVariantMap. Unfortunately, when the JavaScript attempts to update the QVariantMap, the getter function is called again (rather than the setter function).
I can modify the data using a simple helper function such as -
Q_INVOKABLE void Update( QString key, QVariant value ) {
settings[key] = value;
}
I was just wondering if there was a way of doing this without the need for a helper function?
I use QVariantMap for PhantomJS and it works just fine. For example, WebPage#viewportSize is just QVariantMap in its implementation. The usual problem is you can't try to update one of its property only, e.g. viewportSize.width = 300. You have to pass back an object, e.g.:
viewportSize = { width: 300, height: 200 }.
If you need to able to do the former, the only (ugly) workaround that might work is to create a helper object, e.g. Size in the above case, which has the proper setter and getter for the individual property and handle the housekeeping of bridging.

Resources