PDA

View Full Version : apparently impossible qobject_cast is working!?



FS Lover
28th May 2009, 18:41
I have two questions please.
first, I dont understand how this cast from qt's examples (Tree Model Completer) works:


QAbstractItemModel *completionModel = completer->completionModel();
QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(completionModel);

according to qt's class references, QAbstractItemModel doesnt inherit from QAbstractProxyModel.
although indeed completer->completionModel() is a QStandardItemModel, but it makes no deference (it inherits only from QAbstractItemModel that in turn doesnt inherit from QAbstractProxyModel).

complete code of that member function is this:


void MainWindow::highlight(const QModelIndex &index)
{
QAbstractItemModel *completionModel = completer->completionModel();
QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(completionModel);
if (!proxy)
return;
QModelIndex sourceIndex = proxy->mapToSource(index);
treeView->selectionModel()->select(sourceIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
treeView->scrollTo(index);
}

another question is why do we use a QAbstractProxyModel here?
this modified code works too:


void MainWindow::highlight(const QModelIndex &index)
{
/* QAbstractItemModel *completionModel = completer->completionModel();
QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(completionModel);
if (!proxy)
return;
QModelIndex sourceIndex = proxy->mapToSource(index); */
treeView->selectionModel()->select(/* sourceIndex */ index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
treeView->scrollTo(index);
}

qt 4.4.3

caduel
28th May 2009, 18:54
according to qt's class references, QAbstractItemModel doesnt inherit from QAbstractProxyModel.
for qobject_cast to work, QAbstractProxyModel needs to inherit QAbstractItemModel (in your case) - which is the case.


this modified code works too:
pure (bad) luck. the code is not correct.

FS Lover
29th May 2009, 05:29
for qobject_cast to work, QAbstractProxyModel needs to inherit QAbstractItemModel (in your case) - which is the case.

So can we say qobject_cast is a thing much more than other cast instructions?
To my eyes it seems that it somehow creates a new object or indeed extends an existing object.
(sorry if my questions are stupid and taking your time. I am new to qt and c++ both. I need to know these for pushing my c++ and qt view forward quickly.)
Are there any other cast instructions in standard c++ (specially) or qt that work similary?


pure (bad) luck. the code is not correct.
why? I wanted to know the reason.
can you explain for me please?
I have already said: another question is why do we use a QAbstractProxyModel here?
unfortunately qt documentation have not any documentation on this function at all.

wysota
29th May 2009, 08:11
So can we say qobject_cast is a thing much more than other cast instructions?
No, it's more than static_cast<>() but it is a direct equivalence of dynamic_cast<>(), only that it doesn't need compiler's help to do the cast.


To my eyes it seems that it somehow creates a new object or indeed extends an existing object.
It doesn't do anything like that. It just checks the actual inheritance tree using Qt's meta-object system and does the cast of the pointer if it is possible or returns null otherwise.


Are there any other cast instructions in standard c++ (specially) or qt that work similary?
Yes, as already said - dynamic_cast.


why? I wanted to know the reason.
Incidentally the position of the index in the proxy is same as the one in the base model. Pure luck, don't count on it. At best you will get wrong behaviour of the application, at worst it will crash.


I have already said: another question is why do we use a QAbstractProxyModel here?
unfortunately qt documentation have not any documentation on this function at all.

You should ask this question to a person who has written the code. I suspect QSortFilterProxyModel was applied on the view to provide sorting or filtering functionality.

FS Lover
29th May 2009, 14:52
Thank you guru. your texts opened my mind to a threshold required for discovering what I wanted to know.

please make your help complete and see if I am correct:

qt class reference says:
" QAbstractItemModel * QCompleter::completionModel () const
Returns the completion model. The completion model is a read-only list model that contains all the possible matches for the current completion prefix.
The completion model is auto-updated to reflect the current completions. "

extra explanation:
and QCompleter's class reference also says that QCompleter::highlighted() signal's argument is an index to the model returned by completionModel().
so this is the argument passed to our MainWindow::highlight function,
so we have to work with it and map it to the original model (QCompleter::model()) to highlight the item.

IMHO it is somewhat a lack of documentation that haven't mentioned explicitly that completionModel() indeed returns a QAbstractProxyModel (subclass).
but from the text and from some more complicated analysis that seems a little heavy for the very beginers like me, it can be implicitly argued.
Finally, success of the qobject_cast<QAbstractProxyModel *> makes we assured of which
QCompleter::completionModel() returns a QAbstractProxyModel (that in turn inherits from the QAbstractItemModel).

I was wondered why qobject_cast is always successful (not null).

but an unresolved thing!
I don't think that QAbstractProxyModel's inheritance from QAbstractItemModel can make a cast from QAbstractItemModel to QAbstractProxyModel successful.
a QAbstractItemModel doesn't have the function members and properties of a QAbstractProxyModel; it doesn't have such an interface!
that is the QAbstractItemModel that must inherit from the QAbstractProxyModel (which is not the case) for the cast to become successful, not the reverse.
are you sure about that yet?!
if yes, probably we can write some tests to see if such casts are possible.

wysota
29th May 2009, 17:14
IMHO it is somewhat a lack of documentation that haven't mentioned explicitly that completionModel() indeed returns a QAbstractProxyModel (subclass).

No, it's not lack of the documentation. You needn't know there is a proxy there. And you shouldn't try to access the proxy unless you know what you are doing (there might be no proxy there!)


I was wondered why qobject_cast is always successful (not null).
It's not, try this:

QAbstractItemModel *model = new QStringListModel;
QStandardItemModel *smodel = qobject_cast<QStandardItemModel*>(model);
Q_ASSERT(smodel!=0);


I don't think that QAbstractProxyModel's inheritance from QAbstractItemModel can make a cast from QAbstractItemModel to QAbstractProxyModel successful.
a QAbstractItemModel doesn't have the function members and properties of a QAbstractProxyModel; it doesn't have such an interface!
Pure magic, then ;)

FS Lover
30th May 2009, 13:57
It's not, try this:

QAbstractItemModel *model = new QStringListModel;
QStandardItemModel *smodel = qobject_cast<QStandardItemModel*>(model);
Q_ASSERT(smodel!=0);

It's null buddy!
Indeed Q_ASSERT says its null when the prog is compiled in debug mode.
test>mingw32-make debug
...(and took very considerable time!!)
test>debug\test.exe
ASSERT: "smodel!=0" in file main.cpp, line 23

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

qt reference says:
Q_ASSERT() is useful for testing pre- and post-conditions during development. It does nothing if QT_NO_DEBUG was defined during compilation.

you can test for null other ways (a simple if or printing the pointer value), and you will see that its null.

wysota
30th May 2009, 14:26
It's null buddy!

So you see there are cases where qobject_cast doesn't return a valid pointer.

FS Lover
30th May 2009, 16:57
So you see there are cases where qobject_cast doesn't return a valid pointer.
such as when source object doesn't inherit from the cast's destination type.
you can test this for every qt class and all cases in the world, all will be null (impossible to dynamic cast successfully).

the only reason why what completer->completionModel() returns can be cast to a QAbstractProxyModel successfully is that indeed it inherits from or is a subclass of the QAbstractProxyModel.
If you have any doubts about that yet, again we can try to prove it (I think with the help of qt's meta object system I can prove it directly).

FS Lover
30th May 2009, 17:43
#include <QApplication>
#include <QErrorMessage>

#include <QCompleter>
#include <QAbstractProxyModel>

#include <QStringListModel>
#include <QStandardItemModel>

int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QErrorMessage::qtHandler();

QCompleter c;

if(c.completionModel()->inherits("QAbstractProxyModel"))
qDebug("Yes, QCompleter's completionModel inherits QAbstractProxyModel");
else
qDebug("No, QCompleter's completionModel does NOT inherit QAbstractProxyModel");

QStringListModel slm;

if(slm.inherits("QStandardItemModel"))
qDebug("Yes, QStringListModel inherits QStandardItemModel");
else
qDebug("No, QStringListModel does NOT inherit QStandardItemModel");

return app.exec();
}


result:

Yes, QCompleter's completionModel inherits QAbstractProxyModel

No, QStringListModel does NOT inherit QStandardItemModel

wysota
30th May 2009, 17:54
such as when source object doesn't inherit from the cast's destination type.
you can test this for every qt class and all cases in the world, all will be null (impossible to dynamic cast successfully).
That's the point of qobject_cast... it does exactly the same thing dynamic_cast does, only that it doesn't need support from the compiler so it can (among other nice things) make a cast across library boundaries.


the only reason why what completer->completionModel() returns can be cast to a QAbstractProxyModel successfully is that indeed it inherits from or is a subclass of the QAbstractProxyModel.
If you have any doubts about that yet, again we can try to prove it (I think with the help of qt's meta object system I can prove it directly).

I know why the cast succeeds, you don't have to tell me. Either I can't understand the point you are trying to prove or there is nothing to prove and we're just chatting around about nothing.

If, by any chance, you meen that completionModel() should return a pointer to QAbstractProxyModel instead of QAbstractItemModel then the answer is: no, it shouldn't because it's not your business that it is a proxy model, you shouldn't try to touch it in any way; for you it is "some kind of" model and the behaviour can be changed between releases so you shouldn't expect the proxy to always be there.

FS Lover
30th May 2009, 18:34
I know why the cast succeeds, you don't have to tell me. Either I can't understand the point you are trying to prove or there is nothing to prove and we're just chatting around about nothing.

me:

first, I dont understand how this cast from qt's examples (Tree Model Completer) works:

QAbstractItemModel *completionModel = completer->completionModel();
QAbstractProxyModel *proxy = qobject_cast<QAbstractProxyModel *>(completionModel);
according to qt's class references, QAbstractItemModel doesnt inherit from QAbstractProxyModel.

you:

for qobject_cast to work, QAbstractProxyModel needs to inherit QAbstractItemModel (in your case) - which is the case.


such sentences reveal that you had misconceptions.
for a successful qobject_cast/dynamic_cast, source needs to inherit destination. reverse isn't true. we have no such necessary condition you mentioned. it has no relevance to the work of a dynamic cast.

wysota
31st May 2009, 03:52
for a successful qobject_cast/dynamic_cast, source needs to inherit destination. reverse isn't true.

For a successful qobject/dynamic cast an object being casted has to be an instance of the class you are trying to cast it to or its subclass. Period. This is a precise definition with no field for misinterpretation.

QAbstractProxyModel inherits QAbstractItemModel so you can cast an instance of the proxy model from an QAbstractItemModel pointer to QAbstractProxyModel pointer. And I'm sure we both agree to that. And if we do then I have no idea what is the point you are trying to make in this thread.

Assuming you are wondering why some qobject_cast works there is a simple test you can do:

QObject *ptr = <assign the dubious pointer here>;
qDebug() << ptr->metaObject()->className();

FS Lover
31st May 2009, 09:37
No, it's not lack of the documentation. You needn't know there is a proxy there.
some of that qt's own example's functionality relies on knowing there is a proxy there and it makes use of it directly.
And its a common and standard usage.
so, why are you saying we don't need knowing that?!

wysota
31st May 2009, 09:48
so, why are you saying we don't need knowing that?!

Because you shouldn't touch the proxy as the completer relies on modifying it. If you modify the proxy, you will break functionality of the completer. That's why you get a pointer to the interface of the actual class, that's a standard way of doing things in object oriented languages supporting polymorphism.

Bear in mind QAbstractProxyModel is also an interface - the class is abstract so the instance behind the proxy pointer is really a subclass of QAbstractProxyModel (QSortFilterProxyModel probably).

FS Lover
31st May 2009, 10:27
Because you shouldn't touch the proxy as the completer relies on modifying it.
I agree that we shouldn't modify it, but it's sufficiently clear that programmers need to use it directly (of course without modifying it or relying on stability it doesn't have, etc; these must be implemented in the code properly).
what you say is relevant to programming in general that I know already, you know, its abc that every real programmer should know; it is relatively easy to understand and documentations are full of such warnings and advises, since many tools can be misused similarly (specially in languages such as C and C++). but essential programmer's tools never are hidden in order that no one can misuse them!
so I think it is no reason for the documentation not telling that; obviously, we should be aware of the existence of a tool to make use of it.
documentation could add extra explanations and advise about any misuse of a tool, which is very ordinary and frequent in qt and any other such documentations.
mentioning implicit knowledge about that could be a better reasoning.

wysota
1st June 2009, 00:13
I agree that we shouldn't modify it, but it's sufficiently clear that programmers need to use it directly (of course without modifying it or relying on stability it doesn't have, etc; these must be implemented in the code properly).
"Need"? Can you show me a case where you would "need" to use the QAbstractProxyModel API of the completion model ?


what you say is relevant to programming in general that I know already, you know, its abc that every real programmer should know;
There are many things so trivial people forget to do because they are so trivial that everybody knows them so they don't think about them and miss them.


but essential programmer's tools never are hidden in order that no one can misuse them!
Of course they are. For instance the "private" sections of classes in C++ are examples of such case.


so I think it is no reason for the documentation not telling that; obviously, we should be aware of the existence of a tool to make use of it.
No. The case with the completion model is an exact case where you should not know there is a proxy hidden behind. And in the next release of Qt there might not be a proxy there but the API has to remain unchanged.


documentation could add extra explanations and advise about any misuse of a tool, which is very ordinary and frequent in qt and any other such documentations.
mentioning implicit knowledge about that could be a better reasoning.

It's documentation, not a copy of the source code. Take a look at the source code and nothing will be hidden from your eyes.

FS Lover
1st June 2009, 07:06
Can you show me a case where you would "need" to use the QAbstractProxyModel API of the completion model ?

<qt installation dir>/examples/tools/treemodelcompleter/mainwindow.cpp: highlight member function.

every time we need to map the highlighted completion item to the original model the completion is performed for,
we need to use the QAbstractProxyModel API of the completion model.

in this qt's own example we need to do so in order to highlight the user's choice of the completion in the original model.


Of course they are. For instance the "private" sections of classes in C++ are examples of such case.
it is access to them in the programming environment that is restricted, not the knowledge about the existence and function of them in the documentation.
at least for those that programmer can makes use of them (e.g. in subclassing).

wysota
1st June 2009, 07:42
<qt installation dir>/examples/tools/treemodelcompleter/mainwindow.cpp: highlight member function.

every time we need to map the highlighted completion item to the original model the completion is performed for,
we need to use the QAbstractProxyModel API of the completion model.
This is a special case and qobject_cast is here so that you can do the mapping properly. If at all, I would say the completer should return an index from a base model instead of its own internal one but I don't agree it should return a proxy model api.


in this qt's own example we need to do so in order to highlight the user's choice of the completion in the original model.
As you see the example works, so you don't need it.


it is access to them in the programming environment that is restricted, not the knowledge about the existence and function of them in the documentation.
at least for those that programmer can makes use of them (e.g. in subclassing).

No. It's still internal and you shouldn't care.

FS Lover
1st June 2009, 10:09
This is a special case and qobject_cast is here so that you can do the mapping properly.
for what reason do you think it is special?
furthermore, what do you mean from special? what difference does it make regarding such uses we need?
you wanted an example case and I showed you one clear, simple, standard, and completely official one.
I think its clear enough to everyone.
so you have the responsibility to prove any further claim you make here.


I would say the completer should return an index from a base model instead of its own internal one but I don't agree it should return a proxy model api.
base model may be any model that can be potentially complex, costly to process, harder to work with, etc.
but regarding auto completion's suggestions and user's choice among them, many times we need only simple features without any further/external dependencies,
so a simple and shortened model that contains only the relevant items is the best to work with. that is what completionModel gives to us (a simple list model).
but sometimes we need to map indexes of this simple and shortened model to the original model that we want to work with directly.
on the other hand, this tells us what is the set of completion suggestions. this is essential.
so qt's way is the most efficient method. its not improperly designed.


As you see the example works, so you don't need it.
maybe I don't understand your meaning clearly.
can you explain more?
clearly, some feature of the example will not work without that functionality.

wysota
1st June 2009, 10:51
for what reason do you think it is special?
Because here you don't care that it is a proxy, you just want your original model index back. It's a shortcut over using the activated() signal that carries a string and finding that string in your model. So the same functionality could be obtained without qobject_cast with one or two additional lines of code.


what difference does it make regarding such uses we need?
This is not a need where you want to use the completionModel for anything. You just want the original model index back.


you wanted an example case and I showed you one clear, simple, standard, and completely official one.
No, you didn't.


I think its clear enough to everyone.
So file a bug report to Qt Software. They won't change the API because it would break compatibility with existing applications but they might say "you're right, it should have been a QAbstractProxyModel".


so you have the responsibility to prove any further claim you make here.
I don't have a reponsibility to prove anything. You should always use the most general interface to an object and that's a rule of a thumb in OO Software Engineering.


base model may be any model that can be potentially complex, costly to process, harder to work with, etc.
but regarding auto completion's suggestions and user's choice among them, many times we need only simple features without any further/external dependencies,
so a simple and shortened model that contains only the relevant items is the best to work with. that is what completionModel gives to us (a simple list model).

I take it that in your implementation of Qt the source of QCompleter::setModel() doesn't contain a line:

d->proxy->setSourceModel(mode);
which sets the source model of the completion model to the model you pass to it. So sorry, but there is no "simple list model" here. You have all the drawbacks of your original model plus an additional layer that returns the currently used list model to the user. And still you shouldn't care what it does. To you this is just "some" model that can be completely unrelated to your base model, especially if you subclass QCompleter and reimplement setModel - there might not be any proxy there anymore.


maybe I don't understand your meaning clearly.
can you explain more?
clearly, some feature of the example will not work without that functionality.

Examples often use hacks or simplifications. This is one of them.

FS Lover
2nd June 2009, 06:14
It's a shortcut over using the activated() signal that carries a string and finding that string in your model.
`activated ' isn't equal with `highlighted' in functionality.
QCompleter::highlighted:
This signal is sent when an item in the popup() is highlighted by the user.
QCompleter::activated:
This signal is sent when an item in the popup() is activated by the user (by clicking or pressing return).

So file a bug report to Qt Software. They won't change the API because it would break compatibility with existing applications but they might say "you're right, it should have been a QAbstractProxyModel".
it is now a QAbstractProxyModel.
first thing I questioned here was just "why is qobject_cast successful?"
the answer is, because returned object is indeed a QAbstractProxyModel subclass.
but it is not mentioned in the documentation.

You should always use the most general interface to an object and that's a rule of a thumb in OO Software Engineering.
apparently qt's own educational example does not follow that.
so I think you can fill a bug report!!

wysota
2nd June 2009, 10:09
`activated ' isn't equal with `highlighted' in functionality.
QCompleter::highlighted:
This signal is sent when an item in the popup() is highlighted by the user.
QCompleter::activated:
This signal is sent when an item in the popup() is activated by the user (by clicking or pressing return).
Ok, then over "highlighter" carrying a string.


but it is not mentioned in the documentation.
Yes, along with many other things that are not mentioned there.


apparently qt's own educational example does not follow that.

???

Does it expose any API?

nish
2nd June 2009, 10:21
good fight:cool::D:D:D:

FS Lover
3rd June 2009, 06:59
So the same functionality could be obtained without qobject_cast with one or two additional lines of code.
can you please write those two additional lines for that example?
please change the member function and insert the code here so that I can compile and examine it to understand the truth. I am not sure about anything, since I am a newbie anyway.
I think if it's so easy and short you can spend a little time on it to go to the practice and take this debate closer to it's destination. we should try to make it more clear and get benefit from the time and energy we spent here. further the topic will be much more of use for others if we can make a definitive conclusion.
personally I filled a bug report for the documentation (using non-standard method and undocumented API) and I sent the link of this topic for them too. but it's possible that they discard that bug report.

wysota
3rd June 2009, 18:53
I just have to stop answering to all those flames... But what can I say, I do love them...

Use QCompleter::currentCompletion() to find a path in the original model and then use QAbstractItemModel::match() to find the proper index in the model.

A possible implementation:

QStringList path = completer->currentCompletion().split(".");
const QAbstractItemModel *model = treeView->model();
QModelIndex sourceIndex;
while(!path.isEmpty()){
sourceIndex = model->match(model->index(0,0, sourceIndex), Qt::DisplayRole, path.first(), 1).first();
path.removeFirst();
}
treeView->selectionModel()->select(sourceIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
treeView->scrollTo(index);

FS Lover
6th June 2009, 08:20
your code does not work properly.
run the app, type 'p', scroll to 'Parent2'; 'Parent2' does not get selected and 'Parent1' remains selected.

wysota
6th June 2009, 15:15
your code does not work properly.
So improve it :) It's a proof of concept.

FS Lover
16th June 2009, 07:15
this was my submitted bug report: [Issue N255532] using a non-standard method in an example

<qt installation dir>/examples/tools/treemodelcompleter/mainwindow.cpp: highlight member function

it uses completionModel's QAbstractProxyModel interface which is
apparently non-standard and not documented in the class reference.
why an educational example should make use of non-standard methods and
undocumented interfaces?

I received the answer recently:


Basically, the implementation of QCompleter uses a QAbstractProxyModel
subclass to wrap the model, and the writer of the example relied on this
knowledge to write the highlight() function.

Note that the return value of completionModel() is a QAbstractItemModel
pointer purely for generality - it could have been a QAbstractProxyModel
(this would have made things clearer).

I doubt that anyone will change the implementation of this class now, so
we'll go ahead and document the behavior.

Regards,

David
--
David Boddie
Senior Technical Writer
Nokia, Qt Software

FS Lover
16th June 2009, 07:34
additionally, it seems that your suggested method can never differentiate between nodes that have the same text for the completer
and have the same parent too.
and I think indeed no other method than using QAbstractProxyModel's interface can do the job well for all possible cases;
using it is more general and simple.

wysota
16th June 2009, 08:41
But still David agrees with what I said - it is the way it is to expose a more general interface.


additionally, it seems that your suggested method can never differentiate between nodes that have the same text for the completer
and have the same parent too.

Read the docs of the completer class again. It requires nodes (siblings) to be unique.

FS Lover
28th June 2009, 06:32
Read the docs of the completer class again. It requires nodes (siblings) to be unique.
I didn't find such thing. can you show me?

wysota
28th June 2009, 09:28
Quoting completer docs:

QCompleter can look for completions in tree models, assuming that any item (or sub-item or sub-sub-item) can be unambiguously represented as a string by specifying the path to the item.

FS Lover
2nd July 2009, 07:51
so in qt's Tree Model Completer example or any app like that, if we had a source tree like this one:

Parent1
male
fred 23
male
jim 18
female
sara 20
what method do you suggest for getting navigation via qcompleter working properly?
I tested the example with the above tree (corresponding resource file is resources\treemodel.txt) ; it works with the QAbstractProxyModel method perfectly.

wysota
2nd July 2009, 08:52
Just give me a break, please. Do whatever you want.

FS Lover
5th July 2009, 06:38
...
...
...

FS Lover
15th July 2009, 20:50
you can answer for example:
- we shouldn't write such apps (say this to the programmer of the qt's official example too).
- or, we shouldn't have such tree structures :p

wysota
15th July 2009, 22:18
Or we should use qobject_cast. Wait... we do use qobject_cast. So I guess this is fine. Glory for the open-source world.

EOT