Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. General talk
  3. Brainstorm
  4. Why does QVariant use a union?
QtWS25 Last Chance

Why does QVariant use a union?

Scheduled Pinned Locked Moved Unsolved Brainstorm
qvariantdesign
10 Posts 6 Posters 3.9k 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.
  • L Offline
    L Offline
    Larvae
    wrote on 17 Sept 2018, 16:39 last edited by
    #1

    Hi,

    I'm currently looking into working with a QVariant's content without copying and went through the source code. While reading, I starte wondering:

    Why does QVariant use a union to store it's data?

    The union consists of the basic types (char, int, etc) , Object*, void* and PrivateShared* (a pointer to a shared struct containing another void*, i guess for implicit sharing).

    I understand what a union is for, but why doesn't QVariant just use void*. It can be converted into any object or primitive. The type is stored by QVariant anyway since union can't do that. After reading the source code, using only a void* seems much simpler, you just have to add a cast and the QMetaType stuff.

    K J 2 Replies Last reply 17 Sept 2018, 16:54
    0
    • L Larvae
      17 Sept 2018, 16:39

      Hi,

      I'm currently looking into working with a QVariant's content without copying and went through the source code. While reading, I starte wondering:

      Why does QVariant use a union to store it's data?

      The union consists of the basic types (char, int, etc) , Object*, void* and PrivateShared* (a pointer to a shared struct containing another void*, i guess for implicit sharing).

      I understand what a union is for, but why doesn't QVariant just use void*. It can be converted into any object or primitive. The type is stored by QVariant anyway since union can't do that. After reading the source code, using only a void* seems much simpler, you just have to add a cast and the QMetaType stuff.

      K Offline
      K Offline
      koahnig
      wrote on 17 Sept 2018, 16:54 last edited by
      #2

      @Larvae

      IMHO because QVariant does not do a conversion, but simply supplies a different way of access to the same memory.

      Vote the answer(s) that helped you to solve your issue(s)

      1 Reply Last reply
      2
      • H Offline
        H Offline
        hskoglund
        wrote on 17 Sept 2018, 20:18 last edited by
        #3

        Hi, also, for example in a 32-bit Windows Qt program, a void* cannot be converted into a double or a long long (you need 2 *void**s for those).
        So by explicitly declaring all the types as members of that union, the compiler will make sure that sufficient # of bytes are allocated.

        1 Reply Last reply
        2
        • L Offline
          L Offline
          Larvae
          wrote on 17 Sept 2018, 21:25 last edited by Larvae
          #4

          I would call functions to "supply different ways of access to the same memory" as conversion, but then we would be splitting hairs.

          void* doesn't need to have the same size as double or long long. Just cast it to double* and long long* and return *x to do this. It will return a copy, but since QVariant only returns copys and no references or pointers that wouldn't be a problem.

          A quick'n'dirty example would be

          class MyVariant
          {
              void* m_data;
              quint64 m_placeholder;
              size_t m_size;
          
          public:
              template <typename T>
              MyVariant(T &data)
              {
                  qRegisterMetaType<Test>("Test");
          
                  m_size = sizeof (data);
                  //m_data = (void*) malloc(m_size);
                  T* tmp;
          
                  if (m_size < sizeof (m_placeholder)) tmp = new(&m_placeholder) T;
                  else tmp = new T;
          
                  tmp = reinterpret_cast<T*>(data);
                  m_data = tmp;
              }
          
              template <typename T>
              T data()
              {
                  return reinterpret_cast<T>(m_data);
              }
              template <typename T>
              T* pointer()
              {
                  return reinterpret_cast<T*>(&m_data);
              }
              template <typename T>
              T & ref()
              {
                  static T *value;
                  value = pointer<T>();
                  return *value;
              }
          
          };
          

          The quint64 is just to mimik the memory of the union in QVariant. Using a proper allocation could easily store objects of any size. With this, you could

          MyVariant v(*something*);
          T t = v.data<T>();        // get a copy
          T *t = v.pointer<T>();     // get a pointer 
          T& t = v.ref<T>();      // get a reference
          

          Btw I'm working on a class inheriting from QVariant to add working with pointers and references instead of copies. Seems to work pretty good so far.

          A 1 Reply Last reply 18 Sept 2018, 05:48
          0
          • L Larvae
            17 Sept 2018, 21:25

            I would call functions to "supply different ways of access to the same memory" as conversion, but then we would be splitting hairs.

            void* doesn't need to have the same size as double or long long. Just cast it to double* and long long* and return *x to do this. It will return a copy, but since QVariant only returns copys and no references or pointers that wouldn't be a problem.

            A quick'n'dirty example would be

            class MyVariant
            {
                void* m_data;
                quint64 m_placeholder;
                size_t m_size;
            
            public:
                template <typename T>
                MyVariant(T &data)
                {
                    qRegisterMetaType<Test>("Test");
            
                    m_size = sizeof (data);
                    //m_data = (void*) malloc(m_size);
                    T* tmp;
            
                    if (m_size < sizeof (m_placeholder)) tmp = new(&m_placeholder) T;
                    else tmp = new T;
            
                    tmp = reinterpret_cast<T*>(data);
                    m_data = tmp;
                }
            
                template <typename T>
                T data()
                {
                    return reinterpret_cast<T>(m_data);
                }
                template <typename T>
                T* pointer()
                {
                    return reinterpret_cast<T*>(&m_data);
                }
                template <typename T>
                T & ref()
                {
                    static T *value;
                    value = pointer<T>();
                    return *value;
                }
            
            };
            

            The quint64 is just to mimik the memory of the union in QVariant. Using a proper allocation could easily store objects of any size. With this, you could

            MyVariant v(*something*);
            T t = v.data<T>();        // get a copy
            T *t = v.pointer<T>();     // get a pointer 
            T& t = v.ref<T>();      // get a reference
            

            Btw I'm working on a class inheriting from QVariant to add working with pointers and references instead of copies. Seems to work pretty good so far.

            A Offline
            A Offline
            aha_1980
            Lifetime Qt Champion
            wrote on 18 Sept 2018, 05:48 last edited by
            #5

            @Larvae

            Just cast it to double* and long long* and return *x to do this.

            No, small values like int or double are directly stored in the QVariant, there is no memore allocation. Memory allocation always have an overhead, therefore it absolutely makes sense to store small values directly.

            Out of curiosity: Why do you even care?

            Regards

            Qt has to stay free or it will die.

            1 Reply Last reply
            4
            • L Offline
              L Offline
              Larvae
              wrote on 18 Sept 2018, 06:56 last edited by
              #6

              I care out of curiosity. I came accross the problem of accessing the data inside QVariant without copying. Since there is nothing in the documentation and I didn't find any posts I took a peek at the source code (qvariant.h, qvariant_p.h and qvariant.cpp) and wonderd why it's so complicated.

              I know there is overhead for small values, it's just hard to tell how much, since QVariant does a lot of stuff on the side to handle itself.

              The files together are nearly 6000 lines of code and like dozens of small scruct and class definitions. Of course a lot is just comments, but from looking I'd say it's less than 1/3. So I was wondering why something that doesn't seem so difficult is that complicated.

              A 1 Reply Last reply 18 Sept 2018, 08:27
              0
              • L Larvae
                17 Sept 2018, 16:39

                Hi,

                I'm currently looking into working with a QVariant's content without copying and went through the source code. While reading, I starte wondering:

                Why does QVariant use a union to store it's data?

                The union consists of the basic types (char, int, etc) , Object*, void* and PrivateShared* (a pointer to a shared struct containing another void*, i guess for implicit sharing).

                I understand what a union is for, but why doesn't QVariant just use void*. It can be converted into any object or primitive. The type is stored by QVariant anyway since union can't do that. After reading the source code, using only a void* seems much simpler, you just have to add a cast and the QMetaType stuff.

                J Offline
                J Offline
                JonB
                wrote on 18 Sept 2018, 07:44 last edited by
                #7

                @Larvae
                OOI, why do you think that void * is actually a preferable way to store the various possible types than the union you say it uses? The implication of your question is that a single void * which you then convert as needed is better, I'd rather have a union in this case in the first place.

                1 Reply Last reply
                0
                • L Larvae
                  18 Sept 2018, 06:56

                  I care out of curiosity. I came accross the problem of accessing the data inside QVariant without copying. Since there is nothing in the documentation and I didn't find any posts I took a peek at the source code (qvariant.h, qvariant_p.h and qvariant.cpp) and wonderd why it's so complicated.

                  I know there is overhead for small values, it's just hard to tell how much, since QVariant does a lot of stuff on the side to handle itself.

                  The files together are nearly 6000 lines of code and like dozens of small scruct and class definitions. Of course a lot is just comments, but from looking I'd say it's less than 1/3. So I was wondering why something that doesn't seem so difficult is that complicated.

                  A Offline
                  A Offline
                  aha_1980
                  Lifetime Qt Champion
                  wrote on 18 Sept 2018, 08:27 last edited by
                  #8

                  @Larvae said in Why does QVariant use a union?:

                  I know there is overhead for small values, it's just hard to tell how much,

                  Then I recommend you to look into malloc() source code and you will begin to understand... ;)

                  Qt has to stay free or it will die.

                  L 1 Reply Last reply 18 Sept 2018, 16:37
                  3
                  • A aha_1980
                    18 Sept 2018, 08:27

                    @Larvae said in Why does QVariant use a union?:

                    I know there is overhead for small values, it's just hard to tell how much,

                    Then I recommend you to look into malloc() source code and you will begin to understand... ;)

                    L Offline
                    L Offline
                    Larvae
                    wrote on 18 Sept 2018, 16:37 last edited by
                    #9

                    @JonB
                    I didn't mean it like void* is better. I meant why use union over the other if you want to use it with types that are not part of the union anyway? If I would design this, I would either a union and stick with those types (maybe like in boost::variant) or not a union at all if I work with types that aren't in the union. At least based on my current knowledge. But since Qt did it the other way I wanted to know why and hopefully learn something.

                    @aha_1980

                    1. placement new like in my example has far less overhead using the preallocated memory than a simple malloc. And since QVariant allways copies the data there are also allocations for the union (indirectly through the class) anyway.
                    2. I didn't mean the overhead for the malloc() alone but for the whole design. Like I said, there are a lot of helper classes and structs adding to the overhead. E.g. would a QVariant using only void* also need that many helpers or even more? How would the whole design differ in efficiency?

                    Also, why does the union hold a void* and a pointer to a struct with another void* for implicit sharing? The union is already in a private type, why isn't it used for that directly instead of yet another element?

                    J 1 Reply Last reply 18 Sept 2018, 22:59
                    0
                    • L Larvae
                      18 Sept 2018, 16:37

                      @JonB
                      I didn't mean it like void* is better. I meant why use union over the other if you want to use it with types that are not part of the union anyway? If I would design this, I would either a union and stick with those types (maybe like in boost::variant) or not a union at all if I work with types that aren't in the union. At least based on my current knowledge. But since Qt did it the other way I wanted to know why and hopefully learn something.

                      @aha_1980

                      1. placement new like in my example has far less overhead using the preallocated memory than a simple malloc. And since QVariant allways copies the data there are also allocations for the union (indirectly through the class) anyway.
                      2. I didn't mean the overhead for the malloc() alone but for the whole design. Like I said, there are a lot of helper classes and structs adding to the overhead. E.g. would a QVariant using only void* also need that many helpers or even more? How would the whole design differ in efficiency?

                      Also, why does the union hold a void* and a pointer to a struct with another void* for implicit sharing? The union is already in a private type, why isn't it used for that directly instead of yet another element?

                      J Offline
                      J Offline
                      JKSH
                      Moderators
                      wrote on 18 Sept 2018, 22:59 last edited by
                      #10

                      Hi @Larvae, you might get better answers to questions about internal code and design decisions at the Interest mailing list (subscribe first, then post). Qt engineers are active on that list; this forum is mainly used by Qt users who don't necessarily know any internal details.

                      Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                      1 Reply Last reply
                      1

                      5/10

                      18 Sept 2018, 05:48

                      • Login

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