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. Modify QVariant data directly

Modify QVariant data directly

Scheduled Pinned Locked Moved Solved General and Desktop
qvariant
8 Posts 2 Posters 3.3k Views 2 Watching
  • 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 last edited by
    #1

    Hi,

    I'm working on some classes and have a (common) problem with QVariant.

    I want to work with different types at the same time and would like to avoid using some base class with inheritance since i would need wrappers for everything. So I thought about using QVariant.

    The problem is that my classes are intended to store relatively big data elements. Thus, copying them all the time would be a problem, not to mention i would have to pass them pack to my structure manually every time.

    Also, it would be bad because I need to cache my data since files may grow as big as several gb.

    I didn't find anything about how to access the Data stored in QVariant directly, all posts regarding this say you have to copy, so i took a look at the source code.

    QVariant uses a d-pointer which is not private, thus you can inherit from QVariant and add methods to get to the data without copying.

    I looked at

    QVariant::value<T>
    

    first. it calls

    qvariant_cast<T>(*this)
    

    which calls

    QtPrivate::QVariantValueHelperInterface<T>::invoke(v);
    

    QtPrivate is just a namespace. QVariantValueHelperInterface has a few specializations with an invoke method. but QVariantValueHelperInterface<T> doesn't. It inherits from QVariantValueHelper, but that class doesn't have that method too. It should be

    static T invoke(const QVariant &v)
    

    So I looked around some more and found something. Here is a quick draft

    class QxVariant : public QVariant
    {
        template<typename T>
        struct QVariantIntegrator
        {
            static const bool CanUseInternalSpace = sizeof(T) <= sizeof(QVariant::Private::Data)
                    && ((QTypeInfoQuery<T>::isRelocatable) || std::is_enum<T>::value);
            typedef std::integral_constant<bool, CanUseInternalSpace> CanUseInternalSpace_t;
        };
        Q_STATIC_ASSERT(QVariantIntegrator<double>::CanUseInternalSpace);
        Q_STATIC_ASSERT(QVariantIntegrator<long int>::CanUseInternalSpace);
        Q_STATIC_ASSERT(QVariantIntegrator<qulonglong>::CanUseInternalSpace);
    
        #ifdef Q_CC_SUN // Sun CC picks the wrong overload, so introduce awful hack
    
        // takes a type, returns the internal void* pointer cast
        // to a pointer of the input type
        template <typename T>
        static inline T *v_cast(const QVariant::Private *nd, T * = 0)
        {
            QVariant::Private *d = const_cast<QVariant::Private *>(nd);
            return !QVariantIntegrator<T>::CanUseInternalSpace
                    ? static_cast<T *>(d->data.shared->ptr)
                    : static_cast<T *>(static_cast<void *>(&d->data.c));
        }
    
        #else // every other compiler in this world
    
        template <typename T>
        static inline const T *v_cast(const QVariant::Private *d, T * = 0)
        {
            return !QVariantIntegrator<T>::CanUseInternalSpace
                    ? static_cast<const T *>(d->data.shared->ptr)
                    : static_cast<const T *>(static_cast<const void *>(&d->data.c));
        }
    
        template <typename T>
        static inline T *v_cast(QVariant::Private *d, T * = 0)
        {
            return !QVariantIntegrator<T>::CanUseInternalSpace
                    ? static_cast<T *>(d->data.shared->ptr)
                    : static_cast<T *>(static_cast<void *>(&d->data.c));
        }
    
        #endif
    
    public:
        QxVariant(){}
        template <typename T>
        QxVariant(T t) : QVariant(t) {}
    
    
        template <typename T>
        T* pointer()
        {
            QVariant::Private x = (this->d);
            return v_cast<T>(&x);
        }
        template <typename T>
        const T* const_pointer()
        {
            const QVariant::Private x = (this->d);
            return v_cast<T>(&x);
        }
    
        template <typename T>
        T & reference()
        {
            QVariant::Private x = (this->d);
            return *(v_cast<T>(&x));
        }
        template <typename T>
        const T & const_reference()
        {
            const QVariant::Private x = (this->d);
            return *(v_cast<T>(&x));
        }
    
    };
    

    The private part is a copy from qvariant_p.h. I just made v_cast static in here. The rest is just a quick test to see if it works.

    Getting a pointer and a const pointer works for Objects (I tested with QRect), but not with e.g. int.

    The references seem to work for reading the values, but return a copy instead of a reference.

    I hope I can get some help with this here.

    ps: does someone know what the T*=0 in v_cast(const QVariant::Private *d, T * = 0) is good for? It isn't used even once from what i can tell.

    pps: Is there a reason why there is no such method in QVariant in the first place?

    1 Reply Last reply
    0
    • L Offline
      L Offline
      Larvae
      wrote on last edited by
      #2

      update:

      I did some more digging and it seems like i have overseen a few definitions, making things a little easier:

      class QxVariant : public QVariant
      {
      
      public:
          QxVariant(){}
          template <typename T>
          QxVariant(T t) : QVariant(t) {}
      
          template <typename T>
          T* pointer()
          {
              return (this->d.is_shared) 
                      ? static_cast<T *>(this->d.data.shared->ptr)
                      : static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
          }
          
          template <typename T>
          T* const_pointer()
          {
              return (this->d.is_shared) 
                      ? static_cast<T *>(this->d.data.shared->ptr)
                      : static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
          }
          
          template <typename T>
          T & ref()
          {
              return (this->d.is_shared) 
                      ? * static_cast<T *>(this->d.data.shared->ptr)
                      : * static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
          }
          
          template <typename T>
          T & const_ref()
          {
              return (this->d.is_shared) 
                      ? * static_cast<T *>(this->d.data.shared->ptr)
                      : * static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
          }
      
      };
      

      But the behaviour is the same as before. But i found out that the pointer works for QVariant v(T &t), but not with v.fromValue(t);

      Btw, if you want to get the data, QVariant actually defines

      public:
          typedef Private DataPtr;
          inline DataPtr &data_ptr() { return d; }
          inline const DataPtr &data_ptr() const { return d; }
      

      to get it, but it's not documented.

      1 Reply Last reply
      0
      • L Offline
        L Offline
        Larvae
        wrote on last edited by
        #3

        Another update:

        The content of QVariant is inside a union. That union is d.data. It has, aside from the usual char int etc., a member shared, which itself holds a void*. So if d.is_shared is true, the data isn't actually inside the union, not even as pointer.

        If i create the QVariant using QVariant (T &t), is_shared is true, and my pointer function seems to work (for Objects like QRect).

        If it's a primitive like int or set by fromValue(t), is_shared is false and thus it's inside the union. I don't have much experience with unions, but from what i read i can't return a reference to a member of a union since the type is unknown. That might explain why i get a copy in some cases instead of a reference.

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

          Hi,

          What about using QSharedData and its friends to make your data implicitly shared like QByteArray, QImage, etc. ?

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

          1 Reply Last reply
          0
          • L Offline
            L Offline
            Larvae
            wrote on last edited by
            #5

            Hi,
            thanks for your reply. As i understand it, QSharedData relies on inheriting a common class/interface, which is something i would like to avoid since i want to use existing classes without wrappers.

            But i think i found the solution with QVariant myself. I will test it and if everything works fine post it here for others who would like to use it.

            1 Reply Last reply
            0
            • L Offline
              L Offline
              Larvae
              wrote on last edited by
              #6

              Hi

              here is my solution. Was much easier than i thought, kept looking at the wrong places.

              class QxVariant : public QVariant
              {
              
              public:
                  QxVariant(){}
                  template <typename T>
                  QxVariant(T &t) : QVariant(t) {}
                  
                  template <typename T>
                  T* pointer()
                  {
                      return (this->d.is_shared)
                              ? static_cast<T *>(this->d.data.shared->ptr)
                              : static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
                  }
              
                  template <typename T>
                  const T* const_pointer()
                  {
                      return (this->d.is_shared)
                              ? static_cast<T *>(this->d.data.shared->ptr)
                              : static_cast<T *>(static_cast<void *>(&(this->d.data.c)));
                  }
              
                  template <typename T>
                  T & ref()
                  {
                      static T *tRef;
                      tRef = pointer<T>();
                      return *tRef;
                  }
              
                  template <typename T>
                  const T & const_ref()
                  {
                      static T *tRef;
                      tRef = pointer<T>();
                      return *tRef;
                  }
                  
              };
              
              template <typename T>
              class QxVariantPointer
              {
                QxVariant* m_variant;
                
              public:
                QxVariantPointer(QVariant *v) : m_variant(static_cast<QxVariant*>(v)){}
                QxVariantPointer(QxVariant *v) : m_variant(v){}
                
                T* operator ->()
                {
                    return m_variant->pointer<T>();
                }
                
                //other operator overloads
                
              };
              

              The smart pointer is for a more native usage, but didn't want post more operators (too long).

              Fyi this doesn't even use elements from a private header, only the public one. Thus, this should work with all versions.

              I hope some people will find this helpfull or have some improvements.

              1 Reply Last reply
              0
              • L Offline
                L Offline
                Larvae
                wrote on last edited by Larvae
                #7

                Had some time too look into QSharedData. When I first read it (too shallow) and answered, I had misunderstood it's purpose.

                I can say now that it's not what I needed. My problem wasn't about avoiding copying for read only operations and thus can't be prevented using copy on write.

                My problem was about implementing a container like class that holds various types, e.g. like QList<QVariant>, but without copying the data because my container needs to stay the owner to load and unload elements for caching and other stuff.

                That being said, I will definitely take a closer look at QSharedData. I knew about Qt's d-pointers, but didn't know they even provide classes for implementing it. I'm sure I will find use for it sooner than later, so thanks for that information.

                One last question. When I use QSharedData and friends in my class and I want to serialize that class, I just need to define serialization for QSharedDataPointer and the data class, just like normally with other classes?

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

                  It'an implementation detail, you should do it like a normal class.

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

                  1 Reply Last reply
                  0

                  • Login

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