Unsolved Type Erasure In QModelIndex
-
Then derive both from a common virtual base class.
-
Any particular reason for you not to leverage runtime polymorphism and use
dynamic_cast
? -
@kshegunov said in Type Erasure In QModelIndex:
Any particular reason for you not to leverage runtime polymorphism and use
dynamic_cast
?actually i use
dynamic_cast
to solve thi problem for now, but there are too many problem with virtual inherit base class, multi inherit and diamond inherit. and i can NOT modify data struct hierarchies because it not write by me. Before usedynamic_cast
you also have to change void* to a virtual class pointer but some time the real type is not a virtual class -
Hi,
Can you show how you implemented your model ? It looks like you may be taking the problem from the wrong end.
-
@SGaist said in Type Erasure In QModelIndex:
wrong end
the data structure and hierarchy like below
THIS DATA STRUCTURE DEFINE IN OUTER MODULE LIKE DYNMAIC LIBRARY AND MAINTAIN BY OUTER,
NO WAY TO MODIFY OR OPERATE
root(Group*) | |---props--|--prop | |--prop | |--prop | |-- ... | |---group--|--props--|--prop | | |--prop | | |-- ... | | | |--item------props--|--prop | |--item---+ |--prop | |--item---+ |--prop | |--prop |---group--+ |-- ... |---group--+ |--- ... |---item---|--props--|--prop |---item |--prop |---item |--prop |---item |--- ... class Prop // base class { (some property info) }; class Item // base class, can inherited by other class but not Prop or Group { (some data) std::vector<Prop> props; }; class Group // base class { std::vector<Prop> props; std::set<Group*> groups; std::set<Item*> items };
my model implement
// its hard to implement with 2 num 1 void* and above data structure bool IsGroup(const ModelIndex&) { ... } bool IsProps(const ModelIndex&) { ... } QModelIndex MyModel::index(int row, int column, const QModelIndex& parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); if ( IsGroup(parent) ) { // return a props pointer or Group* or Item* ... } else if( IsProps(parent) ) { // return pointer in std::vector data(), not safe ... } else // parentitem is Item* { // return props pointer } return QModelIndex(); } QModelIndex MyModel::parent(const QModelIndex& index) const { // is hard to implement if actual data struct's inclusion relation is one-way just with a row num, a column num and a void * // so i traversal from root to find what it real parent } int MyModel::rowCount(const QModelIndex& parent) const { if (parent.column() > 0) return 0; if ( IsGroup(parentItem) ) { void* parentItem = parent.isValid() ? parent.internalPointer() : root; Group* g = static_cast<Group*>(parentItem); return 1 + g->groups.size() + g->items.size(); } else if( IsProps(parentItem) ) { auto props =static_cast<std::vector<Prop>* >(parent.internalPointer()); return props->size(); } return 1; }
Its not complete code, but I think its clearly.
I want to pass some associate info to QModelIndex, and it would release when QModelIndex release, void* can not do this. QVariant or share_ptr... can do this and not destory original Qt design.
Privade other solution is also welcome, I just want to solute this problem elegantly
Thanks for your help -
@crow said in Type Erasure In QModelIndex:
actually i use dynamic_cast to solve thi problem for now, but there are too many problem with virtual inherit base class, multi inherit and diamond inherit. and i can NOT modify data struct hierarchies because it not write by me. Before use dynamic_cast you also have to change void* to a virtual class pointer but some time the real type is not a virtual class
One thing that comes to mind is the Curiously Recurring Template Pattern (CRTP). It is not the most elegant solution, but I guess it helps when you cannot change the original classes because they are part of a library.
Let's assume you have some library classes:
class SomeLibClassA; class SomeLibClassB; ... // maybe more classes
The
dynamic_cast
needs to be replaced with switch over enums later on. So, define your enum:enum class ClassType { UNKNOWN, SOME_LIB_CLASS_A, SOME_LIB_CLASS_B, ...}; // add enums for all your classes here
For convenience I suggest a comman base class for all CRTP classes:
class Base { public: ClassType type = ClassType::UNKNOWN; };
And now for the CRTP template:
template <class T> class CRTPBase : public Base { public: T *object; };
This can now be used to create wrapper classes for the library classes:
class SomeLibClassAWrapper : public CRTPBase<SomeLibClassA> { public: SomeLibClassAWrapper(SomeLibClassA *o) : type(ClassType::SOME_LIB_CLASS_A), object(o) {} }; class SomeLibClassBWrapper : public CRTPBase<SomeLibClassB> { public: SomeLibClassBWrapper(SomeLibClassB *o) : type(ClassType::SOME_LIB_CLASS_B), object(o) {} }; ... // further lib class wrappers
Now you can use
Base*
instead ofvoid*
orQVariant
.SomeLibClassA *a = new SomeLibClassA(...); // somewhere you create your object ... Base *object = new SomeLibClassAWrapper(a); // you wrap it for your QAbstractItemModel ... switch(object->type) { case ClassType::SOME_LIB_CLASS_A: { SomeLibClassA *a = static_cast<SomeLibClassAWrapper*>(object)->object; ... // do your stuff } break; case ClassType::SOME_LIB_CLASS_B: { SomeLibClassB *b = static_cast<SomeLibClassBWrapper*>(object)->object; ... // do your stuff } break; case ...: // other lib classes }
You see that now you can even use
static_cast
instead ofdynamic_cast
to avoid some additional runtime overhead.You also see that you have to be careful to change a few places when adding another library class you need to use. 1) There's the enum, 2) the wrapper class declaration, and 3) the switch statement (maybe switch statements in multiple places). This makes this solution not elegant in the general case. You have to weigh for yourself if this is a proper solution under the restrictions you have.
-
@SimonSchroeder said in Type Erasure In QModelIndex:
Curiously Recurring Template Pattern
thank for your reply.
i think this mothod is same to Type Erasure. It work on many scene, but not QModelIndex.
Let me show you why.- before create a QModelIndex, we warp a class pointer in a new Class
- we pass new pointer with type info into QModelIndex as a void*
- now we can get right type info
- the question is, how to release this NEW WARP Class pointer? When? If we use a QVatiant, wo can pass object not a pointer would release auto when QModelIndex release
-
@crow said in Type Erasure In QModelIndex:
@SimonSchroeder said in Type Erasure In QModelIndex:
Curiously Recurring Template Pattern
thank for your reply.
i think this mothod is same to Type Erasure. It work on many scene, but not QModelIndex.
Let me show you why.
...
4. the question is, how to release this NEW WARP Class pointer? When? If we use a QVatiant, wo can pass object not a pointer would release auto when QModelIndex releaseQModelIndex is an index into a model. It is not the model.
Make the CRTP, polymorphic, QVariant, or whatever object a part of the model. This object's lifetime should that of the model or at least the item in the model. It may need to be part of a data structure that parallels the referenced code that can't be changed. This is also an opportunity to solve the parent tracing problem alluded to.
-
@jeremy_k said in Type Erasure In QModelIndex:
alluded
so this "Abstract" model just for static model. Image that you have a dynamic tree with thousands of items. As time goes by, thousands of items add to tree, and thousands of items remove. and the model hold item warp index object which have been romove from tree and never use again because model lifetime is at the end. the memory usage must be crazy.
finally, I solve this problem by create a new view/model framework which can handle more hierarchy type. it take time but i need it.
just a litte discussion even not a advice for Qt Framework. Whatever, my problem endThank for your patience
-
@crow said in Type Erasure In QModelIndex:
@jeremy_k said in Type Erasure In QModelIndex:
alluded
I don't know what quoting this word on its own means.
so this "Abstract" model just for static model. Image that you have a dynamic tree with thousands of items. As time goes by, thousands of items add to tree, and thousands of items remove.
This doesn't describe anything that QAbstractItemModel can't be used for. The efficiency of the underlying data structure and access patterns will have an impact on performance, but this is true for data structures in general.
and the model hold item warp index object which have been romove from tree and never use again because model lifetime is at the end. the memory usage must be crazy.
Why is storing the type for a mixed data tree's node in the tree more expensive that storing them separately? If that data is only stored in a temporary index, how is that index constructed in the first place?