QGraphicsTextItem * and void * conversion error
-
I want to use void * to store the pointer of the QGraphicsTextItem object created, and in another function, convert the void * pointer to a QGraphicsItem pointer for easy removal from the scene. However, I found that during the conversion, item and item2 point to different addresses; When converting QGraphicsSimpleTextItem pointers, there is no such problem. May I ask why this is happening or where I am using the problem , here is my demo:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); m_pScene=new QGraphicsScene(this); m_pView=new QGraphicsView(ui->viewWidget); m_pView->setScene(m_pScene); m_pView->setWindowTitle("Graphics View"); m_pView->resize(500, 500); auto x=new QPixmap(R"(D:\desktop\Data\2D\2.bmp)"); pV=new QGraphicsPixmapItem(*x); m_pScene->addItem(pV); //QGraphicsTextItem auto pTextItem=new QGraphicsTextItem("QGraphicsTextItem",pV); auto pItem=(void*)pTextItem; auto items=m_pScene->items(); auto item=(QGraphicsTextItem*)pItem; auto b1=items.contains(item); auto item2=(QGraphicsItem*)pTextItem; auto b2=items.contains(item2); //QGraphicsSimpleTextItem auto pSimpleTextItem=new QGraphicsSimpleTextItem("QGraphicsSimpleTextItem",pV); auto pSimpleItem=(void*)pSimpleTextItem; auto SimpleItems=m_pScene->items(); auto SimpleItem=(QGraphicsTextItem*)pSimpleItem; auto b3=SimpleItems.contains(SimpleItem); auto Simpleitem2=(QGraphicsItem*)pSimpleItem; auto b4=SimpleItems.contains(Simpleitem2); int i=0; }
-
@ksz123 said in QGraphicsTextItem * and void * conversion error:
auto pItem=(void*)pTextItem; auto items=m_pScene->items(); auto item=(QGraphicsTextItem*)pItem; auto b1=items.contains(item); auto item2=(QGraphicsItem*)pTextItem;
Don't use C style casts!
Use reinterpret_cast to cast to/from void*.
Use dynamic_cat to cast in a class hierarchy.How did you validate that item and item2 contain different addresses?
-
Sorry, it should be this demo
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pScene=new QGraphicsScene(this);
m_pView=new QGraphicsView(ui->viewWidget);
m_pView->setScene(m_pScene);m_pView->setWindowTitle("Graphics View"); m_pView->resize(500, 500); auto x=new QPixmap(R"(D:\desktop\Data\2D\2.bmp)"); pV=new QGraphicsPixmapItem(*x); m_pScene->addItem(pV); //QGraphicsTextItem auto pTextItem=new QGraphicsTextItem("QGraphicsTextItem",pV); auto pItem=(void*)pTextItem; auto items=m_pScene->items(); auto item=(QGraphicsItem*)pItem; auto b1=items.contains(item); auto item2=(QGraphicsItem*)pTextItem; auto b2=items.contains(item2); //QGraphicsSimpleTextItem auto pSimpleTextItem=new QGraphicsSimpleTextItem("QGraphicsSimpleTextItem",pV); auto pSimpleItem=(void*)pSimpleTextItem; auto SimpleItems=m_pScene->items(); auto SimpleItem=(QGraphicsItem*)pSimpleItem; auto b3=SimpleItems.contains(SimpleItem); auto Simpleitem2=(QGraphicsItem*)pSimpleItem; auto b4=SimpleItems.contains(Simpleitem2); int i=0;
}
-
@ksz123 said in QGraphicsTextItem * and void * conversion error:
it should be this demo
What should be in this demo?
Please switch to proper C++ casts and tell us how you know that item != item2. -
Because in the initial submission, I was trying to bypass this bug. Secondly, using reinterpret_cast did not improve, and finally, in auto bFlag=item==item2; The value of bFlag is false
//QGraphicsTextItem
auto pTextItem=new QGraphicsTextItem("QGraphicsTextItem",pV);
auto pItem=reinterpret_cast<void*>(pTextItem);auto items=m_pScene->items(); auto item=reinterpret_cast<QGraphicsItem*>(pItem); auto b1=items.contains(item); auto item2=dynamic_cast<QGraphicsItem*>(pTextItem); auto b2=items.contains(item2); auto bFlag=item==item2;
-
@ChrisW67 said in QGraphicsTextItem * and void * conversion error:
@ksz123 said in QGraphicsTextItem * and void * conversion error:
I want to use void * to store the pointer of the QGraphicsTextItem object created, and in another function, convert the void * pointer to a QGraphicsItem pointer
Why?
in order to use c style interface
-
@ksz123
Your question/findings are interesting! :)Here is a standalone reproducible. Please note that in place of the C cast
(void *)
I did tryreinterpret_cast
/static_cast
/dynamic_cast
and they all behaved the same. I have left it with the C-style cast as that is what the OP asked about, but that is not the issue.#include <QApplication> #include <QDebug> #include <QGraphicsView> // #define EXTRA_INCLUDES #ifdef EXTRA_INCLUDES #include <QGraphicsSimpleTextItem> #include <QGraphicsTextItem> #endif int main(int argc, char *argv[]) { QApplication a(argc, argv); QGraphicsScene scene; QGraphicsView view(&scene); QGraphicsSimpleTextItem *simpleTextItem = scene.addSimpleText("Simple Text"); QGraphicsItem *graphicsItemForSimpleText = (QGraphicsItem *)simpleTextItem; void *voidSimpleTextItem = (void *)simpleTextItem; QGraphicsTextItem *textItem = scene.addText("Text"); QGraphicsItem *graphicsItemForText = (QGraphicsItem *)textItem; void *voidTextItem = (void *)textItem; QList<QGraphicsItem *>items = scene.items(Qt::AscendingOrder); // same order as added to scene qDebug() << 1 << items.contains(graphicsItemForSimpleText) << items.contains(voidSimpleTextItem); qDebug() << 2 << items.contains(graphicsItemForText) << items.contains(voidTextItem); #ifdef EXTRA_INCLUDES QGraphicsItem *first = items.at(0); QGraphicsItem *second = items.at(1); qDebug() << 3 << dynamic_cast<QGraphicsSimpleTextItem *>(first) << dynamic_cast<QGraphicsTextItem *>(second); qDebug() << 4 << (items.at(0) == simpleTextItem) << (items.at(1) == textItem); #endif qDebug() << 5 << (items.at(0) == graphicsItemForSimpleText) << (items.at(1) == graphicsItemForText); qDebug() << 6 << (items.at(0) == voidSimpleTextItem) << (items.at(1) == voidTextItem); view.show(); return a.exec(); }
Running this gives output:
1 true true 2 false false 5 true false 6 true false
Uncommenting the
// #define EXTRA_INCLUDES
gives this output:1 true true 2 true false 3 QGraphicsItem(0x555555759c40, pos=0,0) QGraphicsTextItem(0x55555573c9b0, pos=0,0, flags=(ItemUsesExtendedStyleOption)) 4 true true 5 true true 6 true false
In all cases
QGraphicsSimpleTextItem *
works.In both cases for the
QGraphicsTextItem *
thevoidTextItem *
givesfalse
. Without theEXTRA_INCLUDES
definedgraphicsItemForText
also givesfalse
, but with the extra includes it gives true. So that is interesting because whether the compiler has the full definition forQGraphicsTextItem
or just gets told it's aclass QGraphicsTextItem;
affects the result.I suspect that the issue is because, while
QGraphicsSimpleTextItem
is just aQAbstractGraphicsShapeItem
which is aQAbstractGraphicsShapeItem
(single inheritance),QGraphicsTextItem
is aQGraphicsTextItem
which is apublic QObject, public QGraphicsItem
which is multiple inheritance. And I am guessing something goes wrong/gets lost when that is cast tovoid *
.Note also how from output
3
aQGraphicsSimpleTextItem
"degrades" to aQGraphicsItem
but aQGraphicsTextItem
does not and stays "as-is". This too will (I think) be related toQGraphicsTextItem
being "more than just" aQGraphicsItem
because it is aQObject
too.I think this is interesting enough to call on one of out true C++ experts. I invite you to try this standalone code and let us know how you understand the issue of
void *
as it relates toQGraphicsTextItem
, please? :) -
@JonB said in QGraphicsTextItem * and void * conversion error:
This too will (I think) be related to QGraphicsTextItem being "more than just" a QGraphicsItem because it is a QObject too.
Which more or less means that these two classes are 'behind' each other in memory and only the c++ casts know how to do things right since it's c++ and not c.
Don't use c style casts, esp. not when working with objects.
-
@Christian-Ehrlicher said in QGraphicsTextItem * and void * conversion error:
and only the c++ casts know how to do things right since it's c++ and not c.
Whilst I do not disagree that dropping to C/C casts may be problematic, since I wrote/claim above
Please note that in place of the C cast
(void *)
I did tryreinterpret_cast
/static_cast
/dynamic_cast
and they all behaved the same. I have left it with the C-style cast as that is what the OP asked about, but that is not the issue.would you be kind enough to spend a few minutes changing the code in whatever way you like for C++ casts which then "gets things right", as I was unable to do so.... Please, I should like to see how it can be made to work because in my findings I could not, maybe you will do something I did not which will make it work. We are talking about the
void *
case, I cannot get that to work even with C++ casting. In which case I would like an explanation of how it isvoid *
casting rather than C vs C++ casting that is the issue. Thank you.@ksz123
While I still hope to hear exactly why casting tovoid *
messes up, the upshot is going to be that, whether you try C or C++ casting, casting tovoid *
is not going to work. You would only want/need to do that from C. But you clearly need to retain the C++ classes in any casting, so that's not going to work from C (because you cannot declare [I don't mean define] evenclass Something;
to be able to declareSomething *something;
which you would need, as C won't recogniseclass
.) -
@JonB said in QGraphicsTextItem * and void * conversion error:
I suspect that the issue is because, while QGraphicsSimpleTextItem is just a QAbstractGraphicsShapeItem which is a QAbstractGraphicsShapeItem (single inheritance), QGraphicsTextItem is a QGraphicsTextItem which is a public QObject, public QGraphicsItem which is multiple inheritance. And I am guessing something goes wrong/gets lost when that is cast to void *.
You suspect right. Under single inheritance when class B extends class A (which has virtual methods), both class B's
vtable
and memory layout are simple linear extensions of A's. So an upcast (static_cast
explicitly, or what a C++ compiler will turn a C-style cast into) is a no-op as any pointer to B can be safely interepreted as a pointer to A.This is not the case under multiple inheritance, the child class' layout is more complex and divided into "areas" corresponding to the data and
vtable
of each of its' parents. An upcast is no longer a trivial operation, but has to calculate and return a internal pointer to the right "area" (which is of course different to the memory address of the child object as a whole). This blog post has nice visualization of that.This means that a downcast back to the child class is a complex operation, that requires additional information to replace the information lost in the upcast. A round-trip through
void *
would need some sort of a sequence ofstatic_cast
/dynamic_cast
along withreinterpret_cast
incantations... and a lot of care in general. -
P.S.
Assuming I am right about it being multiple inheritance (now confirmed by @IgKh, I wrote this before he replied) which makesQGraphicsTextItem
fail after being cast tovoid *
, I think this whole issue is covered by/explained in multiple inheritance: unexpected result after cast from void * to 2nd base class from 14 years ago!QGraphicsTextItem
derives fromQObject
first andQGraphicsItem
second, hence its layout hasQObject
in first position.For your case changing over to
void *voidTextItem = (void *)(QGraphicsItem *)textItem;
does actually make it work! Whether you can do it (it's too late by the time you pass as
void *
to your code, it has to be done when/before passing the pointer, or you have to know thevoid *
is aQGraphicsTextItem *
and cast it back to that and then viaQGraphicsItem *
to a newvoid *
value, and you won't be able to do that from C code you need C++ to access theclass
es!), and whether you really should abandon your C casts and trying to run stuff from C, is another matter....[To be clear: if you really want to make this work via
void *
from C, you must pass the originalQGraphicsTextItem *textItem
via(QGraphicsItem *)textItem
or equivalent from the C++ layer.] -
@JonB said in QGraphicsTextItem * and void * conversion error:
For your case changing over to
void *voidTextItem = (void *)(QGraphicsItem *)textItem;
Yep, that would a sequence of incantations. Or in C++ speak:
void *voidTextItem = reinterpret_cast<void*>(static_cast<QGraphicsItem*>(textItem))
In general - when a C++ compiler encounters a C-style cast, it will convert it into one of the C++ cast types:
static_cast
,const_cast
,dynamic_cast
orreinterpret_cast
based on what it knows about the relationship between the source and target types. The reason everyone keeps repeating the advice not to use C-style casts in C++ code is because "what the compiler knows about the types" is context dependent and can be surprising (e.g as @JonB has shown, whether there is only a forward declaration or the full definition changes the cast type). Since casts can actually involve machine code being emitted which radically changes the result, being explicit about the cast type is very important. -
Thank you all for your help. Based on your analysis, here is my solution,
Create sectionauto pTextItem=new QGraphicsTextItem("QGraphicsTextItem",pV); auto id=reinterpret_cast<void*>(dynamic_cast<QGraphicsItem*>(pTextItem));
deletion
void DeleteGraphics(void* id) { if (id==nullptr) return; auto scene=m_pQGraphicsView->scene(); auto items=scene->items(); QGraphicsItem* item = static_cast<QGraphicsItem*>(id); if(items.contains(item)){ scene->removeItem(item); delete item; } }
-