QVariant Internals: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
(Formatting fixed)
 
(7 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'''English''' [[QtVariant-SimplifiedChinese|简体中文]] [[QtVariant Bulgarian|Български]]
[[Category:QtInternals]]
== Introduction ==


=QtVariant=
In C++, a variable needs to have its type known at compile time. There are situations that however require us to deal with variables whose type is known only at run time. For example, let's say you have a function that returns a value from the database. What would its return value be? In C, it would be a "void*" and an additional information would provide its type. Or assume that you allow the user to attach arbitrary information to the cells of your custom table widget. What type be the argument type for this function setUserCellData(type x)?
 
==Introduction==
 
In C++, a variable needs to have its type known at compile time. There are situations that however require us to deal with variables whose type is known only at run time. For example, let’s say you have a function that returns a value from the database. What would its return value be? In C, it would be a “void *and an additional information would provide its type. Or assume that you allow the user to attach arbitrary information to the cells of your custom table widget. What type be the argument type for this function setUserCellData(type x)?


A QVariant can be used in the above situations and can be used to hold a value of any type. You can ask a QVariant for its type and handle the value stored inside it appropriately.
A QVariant can be used in the above situations and can be used to hold a value of any type. You can ask a QVariant for its type and handle the value stored inside it appropriately.


Before understanding Qt’s implementation, we will first try to see the various problems involved in designing the QVariant to appreciate the final solution better.
Before understanding Qt's implementation, we will first try to see the various problems involved in designing the QVariant to appreciate the final solution better.
 
==QVariant functionality requirements==


== QVariant functionality requirements ==
# QVariant should not (and cannot) be a template class. If QVariant is a template, every class that store a QVariant needs to become a template.
# QVariant should not (and cannot) be a template class. If QVariant is a template, every class that store a QVariant needs to become a template.
# QVariant must work for both <span class="caps">POD</span> and non-<span class="caps">POD</span> data types. It should work for any type that is copyable and behaves as a value based type. Note that the support for non-<span class="caps">POD</span> types means that one cannot use a C union to hold all known types since C++ disallows placing non-<span class="caps">POD</span> types in a union.
# QVariant must work for both POD and non-POD data types. It should work for any type that is copyable and behaves as a value based type. Note that the support for non-POD types means that one cannot use a C union to hold all known types since C++ disallows placing non-POD types in a union.
# QVariant must be able to store custom user types. So, if you write a MyStruct, you should be able to place it in a QVariant.
# QVariant must be able to store custom user types. So, if you write a MyStruct, you should be able to place it in a QVariant.
# QVariant should (and cannot) not use C++ <span class="caps">RTTI</span>. In C++, <span class="caps">RTTI</span> only works with polymorphic classes.
# QVariant should (and cannot) not use C++ RTTI. In C+'', RTTI only works with polymorphic classes.
 
==Storing the value as ‘void *’==


An idea would be to hold a ‘void *and a type inside QVariant. We can have a QVariant constructor as a function template that can just fit in any type. Like:<br />
== Storing the value as "void*" ==
An idea would be to hold a "void*" and a type inside QVariant. We can have a QVariant constructor as a function template that can just fit in any type. Like:
<code>


However, we will have trouble writing the destructor. How does one ‘delete’ the void pointer? In C++ you cannot delete a ‘void *’.
class QVariant
{
private:
union {
  // POD data types
  int i;
  float f;
  // All non-POD data types are stored as void *.
  void *v;
} data;


==Arriving at the solution==
enum DataType { Integer, Float, NonPod }; // the type of data
int type;


As we can deduce from above, to delete the void * we simply need to know the type. And since QVariant needs to support user defined types it is not possible to put a huge switch case to reinterpret_cast the void * to a specific type and delete the pointer.
public:
// Constuctors for built-in/POD data types
QVariant(int i) {
  data.i = i;
  type = Integer;
}


We also have a problem when trying to access the content of the QVariant. variant.value&lt;MyStruct&gt;() should not crash under any circumstance. If a conversion is not possible, it should return a default constructed MyStruct.
// For all the non-POD types have a template constructor
template <typename T>
QVariant(const T &t) {
  data.v = (void*) new T (t);
  type = NonPod;
//...
}
</code>


The solution is to have a system that can construct a value from a void *, destroy a void * and cast a void * type to access the value. If we have functions for these tasks for ''every'' type that can be stored in a QVariant, then QVariant can use these functions to work on the void *.
However, we will have trouble writing the destructor. How does one 'delete' the void pointer? In C''+ you cannot delete a "void*".


qMetaTypeConstructHelper and qMetaTypeDeleteHelper are the template construction helpers<br /> qRegisterMetaType&lt;MyStruct&gt;(“MyStruct”) —- QMetaType::registerType(“MyStruct”, ctr, dtr); —- Internally it’s all stored in a QVector&lt;QCustomTypeInfo&gt;
== Arriving at the solution ==


If you do qRegisterMetaType for all types, we are done! But it’s a pain?<br /> Q_DECLARE_METATYPE(Type) —— Create a class QMetaTypeId that provides qt_metatype_id() that registers on demand.
As we can deduce from above, to delete the void* we simply need to know the type. And since QVariant needs to support user defined types it is not possible to put a huge switch case to reinterpret_cast the void * to a specific type and delete the pointer.


—- Provides a function
We also have a problem when trying to access the content of the QVariant. variant.value<MyStruct>() should not crash under any circumstance. If a conversion is not possible, it should return a default constructed MyStruct.


qMetaTypeId provides compile time look up of a type’s id
The solution is to have a system that can construct a value from a void ''', destroy a void''' and cast a void * type to access the value. If we have functions for these tasks for ''every'' type that can be stored in a QVariant, then QVariant can use these functions to work on the void *.


===Categories:===
qMetaTypeConstructHelper and qMetaTypeDeleteHelper are the template construction helpers
qRegisterMetaType<MyStruct>("MyStruct")
* QMetaType::registerType("MyStruct", ctr, dtr);
* Internally it's all stored in a QVector<QCustomTypeInfo>


* [[:Category:QtInternals|QtInternals]]
If you do qRegisterMetaType for all types, we are done! But it's a pain?
Q_DECLARE_METATYPE(Type)
* Create a class QMetaTypeId that provides qt_metatype_id() that registers on demand.
* Provides a function

Latest revision as of 13:49, 24 March 2016

Introduction

In C++, a variable needs to have its type known at compile time. There are situations that however require us to deal with variables whose type is known only at run time. For example, let's say you have a function that returns a value from the database. What would its return value be? In C, it would be a "void*" and an additional information would provide its type. Or assume that you allow the user to attach arbitrary information to the cells of your custom table widget. What type be the argument type for this function setUserCellData(type x)?

A QVariant can be used in the above situations and can be used to hold a value of any type. You can ask a QVariant for its type and handle the value stored inside it appropriately.

Before understanding Qt's implementation, we will first try to see the various problems involved in designing the QVariant to appreciate the final solution better.

QVariant functionality requirements

  1. QVariant should not (and cannot) be a template class. If QVariant is a template, every class that store a QVariant needs to become a template.
  2. QVariant must work for both POD and non-POD data types. It should work for any type that is copyable and behaves as a value based type. Note that the support for non-POD types means that one cannot use a C union to hold all known types since C++ disallows placing non-POD types in a union.
  3. QVariant must be able to store custom user types. So, if you write a MyStruct, you should be able to place it in a QVariant.
  4. QVariant should (and cannot) not use C++ RTTI. In C+, RTTI only works with polymorphic classes.

Storing the value as "void*"

An idea would be to hold a "void*" and a type inside QVariant. We can have a QVariant constructor as a function template that can just fit in any type. Like:

class QVariant
{
private:
 union {
  // POD data types
  int i;
  float f;
  // All non-POD data types are stored as void *.
  void *v;
 } data;

 enum DataType { Integer, Float, NonPod }; // the type of data
 int type;

public:
 // Constuctors for built-in/POD data types
 QVariant(int i) {
  data.i = i;
  type = Integer;
 }

 // For all the non-POD types have a template constructor
 template <typename T>
 QVariant(const T &t) {
  data.v = (void*) new T (t);
  type = NonPod;
 //...
}

However, we will have trouble writing the destructor. How does one 'delete' the void pointer? In C+ you cannot delete a "void*".

Arriving at the solution

As we can deduce from above, to delete the void* we simply need to know the type. And since QVariant needs to support user defined types it is not possible to put a huge switch case to reinterpret_cast the void * to a specific type and delete the pointer.

We also have a problem when trying to access the content of the QVariant. variant.value<MyStruct>() should not crash under any circumstance. If a conversion is not possible, it should return a default constructed MyStruct.

The solution is to have a system that can construct a value from a void , destroy a void and cast a void * type to access the value. If we have functions for these tasks for every type that can be stored in a QVariant, then QVariant can use these functions to work on the void *.

qMetaTypeConstructHelper and qMetaTypeDeleteHelper are the template construction helpers qRegisterMetaType<MyStruct>("MyStruct")

  • QMetaType::registerType("MyStruct", ctr, dtr);
  • Internally it's all stored in a QVector<QCustomTypeInfo>

If you do qRegisterMetaType for all types, we are done! But it's a pain? Q_DECLARE_METATYPE(Type)

  • Create a class QMetaTypeId that provides qt_metatype_id() that registers on demand.
  • Provides a function