QVariant Internals/bg: Difference between revisions

From Qt Wiki
Jump to navigation Jump to search
No edit summary
 
No edit summary
Line 1: Line 1:
'''Български''' [[QtVariant|English]] [[QtVariant SimplifiedChinese|简体中文]]<br />[[Category:QtInternals]]<br />[toc align_right=&quot;yes&amp;quot; depth=&quot;3&amp;quot;]
'''Български''' [[QtVariant|English]] [[QtVariant SimplifiedChinese|简体中文]]
[[Category:QtInternals]]
[toc align_right="yes" depth="3"]


= QtVariant =
= QtVariant =
Line 5: Line 7:
== Въведение ==
== Въведение ==


В C++ променливите трябва да имат тип, който да е известен по време на компилацията. Но има ситуации, които налагат да се използват променливи, чийто тип е известен само по време на изпълнение. На пример, нека да кажем, че имате функция, която връща стойност от база данни. Какъв ще бъде типа на върнатата стойност? В C, ще бъде &quot;void '''&quot; и допълнителна информация ще бъде предоставена за типа и. Или да предположим, че вие позволявате на потребителя да добавя произволна информация в клетките на разработена от вас таблица. Какъв тип трябва да бъде аргумента на тази функция - setUserCellData(type x)?
В C++ променливите трябва да имат тип, който да е известен по време на компилацията. Но има ситуации, които налагат да се използват променливи, чийто тип е известен само по време на изпълнение. На пример, нека да кажем, че имате функция, която връща стойност от база данни. Какъв ще бъде типа на върнатата стойност? В C, ще бъде "void '''" и допълнителна информация ще бъде предоставена за типа и. Или да предположим, че вие позволявате на потребителя да добавя произволна информация в клетките на разработена от вас таблица. Какъв тип трябва да бъде аргумента на тази функция - setUserCellData(type x)?
<br />В тези ситуации може да се използва QVariant и той може да съхранява стойност от всеки тип. Вие можете да питате QVariant за типа и да обработите стойността, съхранена вътре подобаващо.
 
<br />Преди да разберем реализацията на Qt, първо ще се опитаме да разберем различните проблеми, включени в дизайна на QVariant, за да оценим по-добре крайното решение.
В тези ситуации може да се използва QVariant и той може да съхранява стойност от всеки тип. Вие можете да питате QVariant за типа и да обработите стойността, съхранена вътре подобаващо.
<br />h2. Изисквания към функционалността на QVariant
 
<br /># QVariant не трябва ( и не може) да бъде шаблонен клас. Ако QVariant е шаблонен, всеки клас, които съхранява QVariant трябва да стане.<br /># QVariant трябва да работи както за POD (http://en.wikipedia.org/wiki/Plain_Old_Data_Structures), така и за не-POD типове данни. Трябва да работи с всеки тип, който може да се копира и да се държи като клас за съхранение на данни. Забележете, че поддръжката на не-POD типове, означава, че не може да се използва C типа ''union'' за съхранение на всички известни типове данни, тъй като C++ не позволява слагането на не-POD типове в ''union''.<br /># QVariant трябва да може да съхранява потребителски типове. Така, че ако напишете MyStruct, трябва да можете да я съхраните в QVariant.<br /># QVariant не трябва (и не може) да използва C++ RTTI. В C+'', RTTI работи само с полиморфични класове.
Преди да разберем реализацията на Qt, първо ще се опитаме да разберем различните проблеми, включени в дизайна на QVariant, за да оценим по-добре крайното решение.
<br />h2. Съхванение на стойността като 'void ''''
 
<br />Идейно би било да се пази 'void'''' и типа в QVariant. Може да имаме QVariant конструктор като функционален шаблон, който да пасва на всеки тип. Нещо като:<br /><code>
h2. Изисквания към функционалността на QVariant
<br />class QVariant<br />{<br />private:<br />union {<br /> // POD типове данни<br /> int i;<br /> float f;<br /> // Всички не-POD типове данни се запазват като void *.<br /> void '''v;<br />} data;
 
<br /> enum DataType { Integer, Float, NonPod }; // типа на данните<br /> int type;<br />public:<br />// Конструктор за вградените/POD типове данни<br />QVariant(int i)<br />{<br /> data.i = i;<br /> type = Integer;<br />}
# QVariant не трябва ( и не може) да бъде шаблонен клас. Ако QVariant е шаблонен, всеки клас, които съхранява QVariant трябва да стане.
<br />// За всички не-POD типове имаме шаблонен конструктор<br />template &lt;typename T&amp;gt;<br />QVariant(const T &amp;amp;t)<br />{<br /> data.v = (void''') new T (t);<br /> type = NonPod;<br /> <br />}<br /></code>
# QVariant трябва да работи както за POD (http://en.wikipedia.org/wiki/Plain_Old_Data_Structures), така и за не-POD типове данни. Трябва да работи с всеки тип, който може да се копира и да се държи като клас за съхранение на данни. Забележете, че поддръжката на не-POD типове, означава, че не може да се използва C типа ''union'' за съхранение на всички известни типове данни, тъй като C++ не позволява слагането на не-POD типове в ''union''.
<br />Обаче имаме проблем, когато трябва да направим деструктор. Как да 'delete' void указател? В C''+ не можете да унищожавате 'void''''.
# QVariant трябва да може да съхранява потребителски типове. Така, че ако напишете MyStruct, трябва да можете да я съхраните в QVariant.
# QVariant не трябва (и не може) да използва C++ RTTI. В C+'', RTTI работи само с полиморфични класове.
 
h2. Съхванение на стойността като 'void ''''
 
Идейно би било да се пази 'void'''' и типа в QVariant. Може да имаме QVariant конструктор като функционален шаблон, който да пасва на всеки тип. Нещо като:
<code>
 
class QVariant
{
private:
union {
// POD типове данни
int i;
float f;
// Всички не-POD типове данни се запазват като void *.
void '''v;
} data;
 
enum DataType { Integer, Float, NonPod }; // типа на данните
int type;
public:
// Конструктор за вградените/POD типове данни
QVariant(int i)
{
data.i = i;
type = Integer;
}
 
// За всички не-POD типове имаме шаблонен конструктор
template <typename T>
QVariant(const T &amp;amp;t)
{
data.v = (void''') new T (t);
type = NonPod;
}
</code>
 
Обаче имаме проблем, когато трябва да направим деструктор. Как да 'delete' void указател? В C''+ не можете да унищожавате 'void''''.


== Намиране на решението ==
== Намиране на решението ==
Line 21: Line 62:
Както можем да се досетим от горния пример, за да унищожим void ''', ние просто трябва да знаем типа. И тъй като QVariant трябва да поддържа дефинирани от потребителя типове, не е възможно да се сложи един голям ''switch'' с reinterpret_cast на void''' към даден тип и да унищожим указателя.
Както можем да се досетим от горния пример, за да унищожим void ''', ние просто трябва да знаем типа. И тъй като QVariant трябва да поддържа дефинирани от потребителя типове, не е възможно да се сложи един голям ''switch'' с reinterpret_cast на void''' към даден тип и да унищожим указателя.


Също имаме и проблем, когато искаме да вземем съдържанието на QVariant. variant.value&amp;lt;MyStruct&amp;gt;() не трябва да дава грешка при никакви обстоятелства. Ако конвертирането не е възможно, трябва да върнем променлива от тип MyStruct, създадена с конструктора му по подразбиране.
Също имаме и проблем, когато искаме да вземем съдържанието на QVariant. variant.value<MyStruct>() не трябва да дава грешка при никакви обстоятелства. Ако конвертирането не е възможно, трябва да върнем променлива от тип MyStruct, създадена с конструктора му по подразбиране.


Решението е да имаме система, която може да конструира стойност от void ''', да унищожава void''' и да превръща void * в правилния тип, за да може да работи със стойността. Ако имаме функции за тези дейности за ''всеки'' тип, който може да се съхранява в QVariant, тогава QVariant може да използва тези функции за да работи с void *.
Решението е да имаме система, която може да конструира стойност от void ''', да унищожава void''' и да превръща void * в правилния тип, за да може да работи със стойността. Ако имаме функции за тези дейности за ''всеки'' тип, който може да се съхранява в QVariant, тогава QVariant може да използва тези функции за да работи с void *.


qMetaTypeConstructHelper и qMetaTypeDeleteHelper са помощни шаблонни функции<br />qRegisterMetaType&amp;lt;MyStruct&amp;gt;(&quot;MyStruct&amp;quot;)<br /> — QMetaType::registerType(&quot;MyStruct&amp;quot;, ctr, dtr);<br /> — Вътрешно всичко се пази в QVector&amp;lt;QCustomTypeInfo&amp;gt;
qMetaTypeConstructHelper и qMetaTypeDeleteHelper са помощни шаблонни функции
qRegisterMetaType<MyStruct>("MyStruct")
— QMetaType::registerType("MyStruct", ctr, dtr);
— Вътрешно всичко се пази в QVector<QCustomTypeInfo>


Ако направим qRegisterMetaType за всички типове, ще сме готови! Но това не е ли прекалено много работа?<br />Q_DECLARE_METATYPE(Type)<br /> —- Създава клас QMetaTypeId, който предоставя qt_metatype_id(), което регистрира при нужда.
Ако направим qRegisterMetaType за всички типове, ще сме готови! Но това не е ли прекалено много работа?
Q_DECLARE_METATYPE(Type)
—- Създава клас QMetaTypeId, който предоставя qt_metatype_id(), което регистрира при нужда.


— Предоставя функция
— Предоставя функция

Revision as of 13:33, 25 February 2015

Български English 简体中文 [toc align_right="yes" depth="3"]

QtVariant

Въведение

В C++ променливите трябва да имат тип, който да е известен по време на компилацията. Но има ситуации, които налагат да се използват променливи, чийто тип е известен само по време на изпълнение. На пример, нека да кажем, че имате функция, която връща стойност от база данни. Какъв ще бъде типа на върнатата стойност? В C, ще бъде "void " и допълнителна информация ще бъде предоставена за типа и. Или да предположим, че вие позволявате на потребителя да добавя произволна информация в клетките на разработена от вас таблица. Какъв тип трябва да бъде аргумента на тази функция - setUserCellData(type x)?

В тези ситуации може да се използва QVariant и той може да съхранява стойност от всеки тип. Вие можете да питате QVariant за типа и да обработите стойността, съхранена вътре подобаващо.

Преди да разберем реализацията на Qt, първо ще се опитаме да разберем различните проблеми, включени в дизайна на QVariant, за да оценим по-добре крайното решение.

h2. Изисквания към функционалността на QVariant

  1. QVariant не трябва ( и не може) да бъде шаблонен клас. Ако QVariant е шаблонен, всеки клас, които съхранява QVariant трябва да стане.
  2. QVariant трябва да работи както за POD (http://en.wikipedia.org/wiki/Plain_Old_Data_Structures), така и за не-POD типове данни. Трябва да работи с всеки тип, който може да се копира и да се държи като клас за съхранение на данни. Забележете, че поддръжката на не-POD типове, означава, че не може да се използва C типа union за съхранение на всички известни типове данни, тъй като C++ не позволява слагането на не-POD типове в union.
  3. QVariant трябва да може да съхранява потребителски типове. Така, че ако напишете MyStruct, трябва да можете да я съхраните в QVariant.
  4. QVariant не трябва (и не може) да използва C++ RTTI. В C+, RTTI работи само с полиморфични класове.

h2. Съхванение на стойността като 'void '

Идейно би било да се пази 'void' и типа в QVariant. Може да имаме QVariant конструктор като функционален шаблон, който да пасва на всеки тип. Нещо като:

class QVariant
{
private:
union {
 // POD типове данни
 int i;
 float f;
 // Всички не-POD типове данни се запазват като void *.
 void '''v;
} data;

 enum DataType { Integer, Float, NonPod }; // типа на данните
 int type;
public:
// Конструктор за вградените/POD типове данни
QVariant(int i)
{
 data.i = i;
 type = Integer;
}

// За всички не-POD типове имаме шаблонен конструктор
template <typename T>
QVariant(const T &amp;amp;t)
{
 data.v = (void''') new T (t);
 type = NonPod;
 
}

Обаче имаме проблем, когато трябва да направим деструктор. Как да 'delete' void указател? В C+ не можете да унищожавате 'void''.

Намиране на решението

Както можем да се досетим от горния пример, за да унищожим void , ние просто трябва да знаем типа. И тъй като QVariant трябва да поддържа дефинирани от потребителя типове, не е възможно да се сложи един голям switch с reinterpret_cast на void към даден тип и да унищожим указателя.

Също имаме и проблем, когато искаме да вземем съдържанието на QVariant. variant.value<MyStruct>() не трябва да дава грешка при никакви обстоятелства. Ако конвертирането не е възможно, трябва да върнем променлива от тип MyStruct, създадена с конструктора му по подразбиране.

Решението е да имаме система, която може да конструира стойност от void , да унищожава void и да превръща void * в правилния тип, за да може да работи със стойността. Ако имаме функции за тези дейности за всеки тип, който може да се съхранява в QVariant, тогава QVariant може да използва тези функции за да работи с void *.

qMetaTypeConstructHelper и qMetaTypeDeleteHelper са помощни шаблонни функции qRegisterMetaType<MyStruct>("MyStruct")

— QMetaType::registerType("MyStruct", ctr, dtr);
— Вътрешно всичко се пази в QVector<QCustomTypeInfo>

Ако направим qRegisterMetaType за всички типове, ще сме готови! Но това не е ли прекалено много работа? Q_DECLARE_METATYPE(Type)

—- Създава клас QMetaTypeId, който предоставя qt_metatype_id(), което регистрира при нужда.

— Предоставя функция