Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Type Erasure In QModelIndex
QtWS25 Last Chance

Type Erasure In QModelIndex

Scheduled Pinned Locked Moved Unsolved General and Desktop
14 Posts 6 Posters 781 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    crow
    wrote on last edited by
    #1

    I create my custom model by QAbstractItemModel. The Qt model use QModelIndex just with row, column and a void* pointer as abstrauct index to actual data. it works well when the actual all data struct have same base virtual class.
    But I meet a problem that my actual data structs have different base class. I can not modify the data struct code, they from other module.
    If QModelIndex can replace internal pointer from void* to QVariant that I can pass the struct after type erasure. I try change Qt source code and compile, it work well.
    Can Qt accpet this change or provade other to QModelIndex so user can identify what actual type internal pointer is. I think its useful

    thank for your read

    kshegunovK 1 Reply Last reply
    0
    • Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #2

      This change will not be accepted and it's not needed at all. There is no difference between storing a pointer as void* or in a QVariant.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      C 1 Reply Last reply
      1
      • C crow

        I create my custom model by QAbstractItemModel. The Qt model use QModelIndex just with row, column and a void* pointer as abstrauct index to actual data. it works well when the actual all data struct have same base virtual class.
        But I meet a problem that my actual data structs have different base class. I can not modify the data struct code, they from other module.
        If QModelIndex can replace internal pointer from void* to QVariant that I can pass the struct after type erasure. I try change Qt source code and compile, it work well.
        Can Qt accpet this change or provade other to QModelIndex so user can identify what actual type internal pointer is. I think its useful

        thank for your read

        kshegunovK Offline
        kshegunovK Offline
        kshegunov
        Moderators
        wrote on last edited by
        #3

        ... or using an abstract indexing of quintptr type ... you can save references to your objects however you desire and provide the indexing through the overload that takes an integer type.

        Read and abide by the Qt Code of Conduct

        1 Reply Last reply
        0
        • Christian EhrlicherC Christian Ehrlicher

          This change will not be accepted and it's not needed at all. There is no difference between storing a pointer as void* or in a QVariant.

          C Offline
          C Offline
          crow
          wrote on last edited by
          #4

          @Christian-Ehrlicher a void* pinter delete type info, If pass the struct pointer after type erasure, It can not find a suitable time to release passed struct pointer.

          class File
          {
              const char* GetData() const ...
          }
          
          class Folder
          {
              const char* GetName() const ...
          
             std::set<File*> fileset;
             std::set<Folder*> folder;
          }
          

          just like above, wo want to show the folder file like real file struct. unfortunately, only a void* can not identify it is a folder or a file because they have different base class. and there are many time the data and the hierarchies is from other module so wo can change it. but if use QVariant we can add type info in to QVariant.
          actually I meet this problem in my work, void* pointer can not identify what actual type is but QVariant can. I have to wrtite ugly and complex code to show this hierarchies
          I think this change is tiny and have no bad effect for dev and runtime efficiency, It can improve QAbstrauctItemModel`s compatibility

          kshegunovK 1 Reply Last reply
          0
          • Christian EhrlicherC Offline
            Christian EhrlicherC Offline
            Christian Ehrlicher
            Lifetime Qt Champion
            wrote on last edited by
            #5

            Then derive both from a common virtual base class.

            Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
            Visit the Qt Academy at https://academy.qt.io/catalog

            1 Reply Last reply
            1
            • C crow

              @Christian-Ehrlicher a void* pinter delete type info, If pass the struct pointer after type erasure, It can not find a suitable time to release passed struct pointer.

              class File
              {
                  const char* GetData() const ...
              }
              
              class Folder
              {
                  const char* GetName() const ...
              
                 std::set<File*> fileset;
                 std::set<Folder*> folder;
              }
              

              just like above, wo want to show the folder file like real file struct. unfortunately, only a void* can not identify it is a folder or a file because they have different base class. and there are many time the data and the hierarchies is from other module so wo can change it. but if use QVariant we can add type info in to QVariant.
              actually I meet this problem in my work, void* pointer can not identify what actual type is but QVariant can. I have to wrtite ugly and complex code to show this hierarchies
              I think this change is tiny and have no bad effect for dev and runtime efficiency, It can improve QAbstrauctItemModel`s compatibility

              kshegunovK Offline
              kshegunovK Offline
              kshegunov
              Moderators
              wrote on last edited by
              #6

              Any particular reason for you not to leverage runtime polymorphism and use dynamic_cast?

              Read and abide by the Qt Code of Conduct

              C 1 Reply Last reply
              0
              • kshegunovK kshegunov

                Any particular reason for you not to leverage runtime polymorphism and use dynamic_cast?

                C Offline
                C Offline
                crow
                wrote on last edited by
                #7

                @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 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

                S 1 Reply Last reply
                0
                • SGaistS Offline
                  SGaistS Offline
                  SGaist
                  Lifetime Qt Champion
                  wrote on last edited by
                  #8

                  Hi,

                  Can you show how you implemented your model ? It looks like you may be taking the problem from the wrong end.

                  Interested in AI ? www.idiap.ch
                  Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                  C 1 Reply Last reply
                  1
                  • SGaistS SGaist

                    Hi,

                    Can you show how you implemented your model ? It looks like you may be taking the problem from the wrong end.

                    C Offline
                    C Offline
                    crow
                    wrote on last edited by
                    #9

                    @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

                    1 Reply Last reply
                    0
                    • C crow

                      @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 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

                      S Offline
                      S Offline
                      SimonSchroeder
                      wrote on last edited by
                      #10

                      @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 of void* or QVariant.

                      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 of dynamic_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.

                      C 1 Reply Last reply
                      0
                      • S SimonSchroeder

                        @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 of void* or QVariant.

                        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 of dynamic_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.

                        C Offline
                        C Offline
                        crow
                        wrote on last edited by
                        #11

                        @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.

                        1. before create a QModelIndex, we warp a class pointer in a new Class
                        2. we pass new pointer with type info into QModelIndex as a void*
                        3. now we can get right type info
                        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 release
                        jeremy_kJ 1 Reply Last reply
                        0
                        • C crow

                          @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.

                          1. before create a QModelIndex, we warp a class pointer in a new Class
                          2. we pass new pointer with type info into QModelIndex as a void*
                          3. now we can get right type info
                          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 release
                          jeremy_kJ Offline
                          jeremy_kJ Offline
                          jeremy_k
                          wrote on last edited by
                          #12

                          @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 release

                          QModelIndex 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.

                          Asking a question about code? http://eel.is/iso-c++/testcase/

                          C 1 Reply Last reply
                          1
                          • jeremy_kJ jeremy_k

                            @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 release

                            QModelIndex 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.

                            C Offline
                            C Offline
                            crow
                            wrote on last edited by
                            #13

                            @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 end

                            Thank for your patience

                            jeremy_kJ 1 Reply Last reply
                            1
                            • C crow

                              @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 end

                              Thank for your patience

                              jeremy_kJ Offline
                              jeremy_kJ Offline
                              jeremy_k
                              wrote on last edited by
                              #14

                              @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?

                              Asking a question about code? http://eel.is/iso-c++/testcase/

                              1 Reply Last reply
                              1

                              • Login

                              • Login or register to search.
                              • First post
                                Last post
                              0
                              • Categories
                              • Recent
                              • Tags
                              • Popular
                              • Users
                              • Groups
                              • Search
                              • Get Qt Extensions
                              • Unsolved