وبغض النظر عن مدى فخرتي بالعمارة المبنية ، يجب أن أعترف - لقد تبين أن التنفيذ ، بصراحة ، مثير للجدل.
كل هذا أدى إلى معالجة واسعة النطاق ، ستتم مناقشة نتائجها في هذه المقالة. لمزيد من التفاصيل - تحت الخفض!
مات QSerializer
كان لـ QSerializer عيوب ، والتي غالبًا ما أصبح حلها عيبًا أكبر ، وهنا عدد قليل منها:
- باهظ الثمن (التسلسل ، الاحتفاظ بحراس الممتلكات في الكومة ، والتحكم في عمر الحراس ، وما إلى ذلك)
- العمل فقط مع الفئات المستندة إلى QObject
- يجب أن تستند الكائنات "المعقدة" المتداخلة ومجموعاتها أيضًا إلى QObject
- عدم القدرة على استكمال المجموعات أثناء إلغاء التسلسل
- فقط من الناحية النظرية التعشيش اللانهائي
- عدم القدرة على العمل مع أنواع مهمة من الكائنات "المعقدة" ، بسبب حظر النسخ من QObject
- الحاجة إلى التسجيل الإلزامي للأنواع في نظام Qt meta-object
- مشاكل "المكتبة" الشائعة مثل الارتباط وقابلية النقل بين الأنظمة الأساسية
من بين أشياء أخرى ، أردت أن أكون قادرًا على إجراء تسلسل لأي كائن "هنا والآن" ، عندما كان علي استخدام ربط ضخم للطرق في مساحة الاسم QSerializer.
تحيا QSerializer!
لم يكن QSerializer كاملاً. كان من الضروري التوصل إلى حل لا يعتمد فيه المستخدم على QObject ، سيكون من الممكن العمل مع أنواع القيمة وبتكلفة أقل.
في تعليق على المقال السابق ، المستخدمميكرولالاحظت أنه يمكنك التفكير في استخدام Q_GADGET .
مزايا Q_GADGET :
- لا قيود على النسخ
- له نسخة ثابتة من QMetaObject للوصول إلى الخصائص
بالاعتماد على Q_GADGET ، اضطررت إلى إعادة النظر في النهج المتبع في كيفية إنشاء JSON و XML استنادًا إلى حقول الفئة المعلنة. ظهرت مشكلة "التكلفة الباهظة" في المقام الأول بسبب:
- حجم فئة التخزين الكبير (40 بايت على الأقل)
- تخصيص كومة للكيانات الوصي الجديد لكل ملكية والتحكم في مدة البقاء (TTL) الخاصة بهم
لتقليل التكلفة ، قمت بصياغة المتطلبات التالية:
التواجد في كل كائن قابل للتسلسل لطرق الأسلاك للتسلسل / إلغاء التسلسل لجميع خصائص الفئة ووجود طرق لقراءة القيم وكتابتها لكل خاصية باستخدام التنسيق المخصص لهذه الخاصية
وحدات الماكرو
إن التغلب على الكتابة القوية لـ C ++ ، والتي تعقد عملية التسلسل التلقائي ، ليس بالأمر السهل ، وقد أظهرت التجربة السابقة ذلك. من ناحية أخرى ، يمكن أن تكون وحدات الماكرو مساعدة كبيرة في حل مثل هذه المشكلة (تقريبًا نظام كيو تي meta-object بأكمله مبني على وحدات الماكرو) ، لأنه باستخدام وحدات الماكرو ، يمكنك إنشاء التعليمات البرمجية للأساليب والخصائص.
نعم ، غالبًا ما تكون وحدات الماكرو شريرة في أنقى صورها - يكاد يكون من المستحيل تصحيحها. يمكنني مقارنة كتابة ماكرو لتوليد رمز بوضع حذاء من الكريستال على كعب رئيسك في العمل ، لكن الصعوبة لا تعني المستحيل!
استطرادا غنائي حول وحدات الماكرو
— , , «» (). .
يوفر QSerializer حاليًا طريقتين للإعلان عن فئة على أنها قابلة للتسلسل: ترث من فئة QSerializer أو استخدام ماكرو إنشاء كود QS_CLASS .
بادئ ذي بدء ، تحتاج إلى تعريف ماكرو Q_GADGET في جسم الفئة ، وهذا يتيح الوصول إلى staticMetaObject ، وسوف يخزن الخصائص التي تم إنشاؤها بواسطة وحدات الماكرو.
سيسمح لك الوراثة من QSerializer بنقل كائنات متعددة قابلة للتسلسل إلى نوع واحد وتسلسلها بكميات كبيرة.
تحتوي فئة QSerializer على 4 طرق explorer التي تتيح لك تحليل خصائص كائن وطريقة افتراضية واحدة للحصول على مثيل QMetaObject:
QJsonValue toJson() const
void fromJson(const QJsonValue &)
QDomNode toXml() const
void fromXml(const QDomNode &)
virtual const QMetaObject * metaObject() const
لا يحتوي Q_GADGET على كل ربط كائن التعريف الذي يوفره Q_OBJECT .
داخل QSerializer ، سيمثل مثيل staticMetaObject فئة QSerializer ، ولكن لا يشتق منها بأي شكل من الأشكال ، لذلك عند إنشاء فئة قائمة على QSerializer ، يجب تجاوز طريقة metaObject. يمكنك إضافة ماكرو QS_SERIALIZER إلى نص الفصل وستتجاوز طريقة metaObject نيابة عنك.
أيضًا ، باستخدام staticMetaObject بدلاً من تخزين مثيل QMetaObject في كل كائن يوفر 40 بايت من حجم الفصل ، حسنًا ، بشكل عام ، الجمال!
إذا كنت لا تريد أن ترث لسبب ما ، فيمكنك تحديد ماكرو QS_CLASS في جسم الفئة المتسلسلة، سيولد جميع الطرق المطلوبة بدلاً من التوريث من QSerializer.
إعلان المجالات
بشكل منفصل ، هناك 4 أنواع من البيانات القابلة للتسلسل في JSON و XML ، والتي بدونها لن يكتمل التسلسل لهذه التنسيقات. يوضح الجدول أنواع البيانات ووحدات الماكرو المقابلة كطريقة لوصف:
| نوع البيانات | وصف | دقيق |
|---|---|---|
| حقل | حقل عادي من النوع البدائي (أرقام مختلفة ، سلاسل ، أعلام) | QS_FIELD |
| مجموعة | مجموعة من القيم لأنواع البيانات البدائية | QS_COLLECTION |
| شيء | البنية المعقدة للحقول أو الهياكل المعقدة الأخرى | QS_OBJECT |
| مجموعة من الأشياء | مجموعة من هياكل البيانات المعقدة من نفس النوع | QS_COLLECTION_OBJECTS |
سنفترض أن الكود الذي ينشئ وحدات الماكرو هذه يسمى وصفًا ، وتسمى وحدات الماكرو التي تنشئها بالوصف.
يوجد مبدأ واحد فقط لتوليد الوصف - لحقل معين ، قم بإنشاء خاصية JSON و XML وحدد طرق كتابة / قراءة القيم.
دعنا نحلل إنشاء وصف JSON باستخدام مثال حقل نوع البيانات البدائي:
/* Create JSON property and methods for primitive type field*/
#define QS_JSON_FIELD(type, name)
Q_PROPERTY(QJsonValue name READ get_json_##name WRITE set_json_##name)
private:
QJsonValue get_json_##name() const {
QJsonValue val = QJsonValue::fromVariant(QVariant(name));
return val;
}
void set_json_##name(const QJsonValue & varname){
name = varname.toVariant().value<type>();
}
...
int digit;
QS_JSON_FIELD(int, digit)
بالنسبة لحقل الرقم الدولي ، سيتم إنشاء رقم خاصية مع نوع QJsonValue وسيتم تحديد طرق الكتابة والقراءة الخاصة - get_json_digit و set_json_digit - ، وستصبح موصلات لتسلسل / إلغاء تسلسل حقل الرقم باستخدام JSON.
كيف يحدث هذا؟
name digit, ('##') digit — .
type int. , type int . QVariant int .
type int. , type int . QVariant int .
وإليك إنشاء وصف JSON لهيكل معقد:
/* Generate JSON-property and methods for some custom class */
/* Custom type must be provide methods fromJson and toJson */
#define QS_JSON_OBJECT(type, name)
Q_PROPERTY(QJsonValue name READ get_json_##name WRITE set_json_##name)
private:
QJsonValue get_json_##name() const {
QJsonObject val = name.toJson();
return QJsonValue(val);
}
void set_json_##name(const QJsonValue & varname) {
if(!varname.isObject())
return;
name.fromJson(varname);
}
...
SomeClass object;
QS_JSON_OBJECT(SomeClass, object)
الكائنات المعقدة هي مجموعة من الخصائص المتداخلة التي ستعمل كخاصية واحدة "كبيرة" لفئة خارجية ، لأن هذه الكائنات سيكون لها أيضًا طرق سلكية. كل ما عليك القيام به من أجل ذلك هو استدعاء طريقة الدليل المناسبة في طرق القراءة والكتابة للهياكل المعقدة.
خلق الطبقة
وبالتالي ، لدينا بنية أساسية بسيطة إلى حد ما لإنشاء فئة قابلة للتسلسل.
لذلك ، على سبيل المثال ، يمكنك جعل فئة قابلة للتسلسل عن طريق التوريث من QSerializer:
class SerializableClass : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
};
أو هكذا ، باستخدام ماكرو QS_CLASS :
class SerializableClass {
Q_GADGET
QS_CLASS
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
};
مثال على التسلسل JSON
:
, :
JSON:
— , XML , toJson toXml.
example.
class CustomType : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, someInteger)
QS_FIELD(QString, someString)
};
class SerializableClass : public QSerializer {
Q_GADGET
QS_SERIALIZER
QS_FIELD(int, digit)
QS_COLLECTION(QList, QString, strings)
QS_OBJECT(CustomType, someObject)
QS_COLLECTION_OBJECTS(QVector, CustomType, objects)
};
, :
SerializableClass serializable;
serializable.someObject.someString = "ObjectString";
serializable.someObject.someInteger = 99999;
for(int i = 0; i < 3; i++) {
serializable.digit = i;
serializable.strings.append(QString("list of strings with index %1").arg(i));
serializable.objects.append(serializable.someObject);
}
QJsonObject json = serializable.toJson();
JSON:
{
"digit": 2,
"objects": [
{
"someInteger": 99999,
"someString": "ObjectString"
},
{
"someInteger": 99999,
"someString": "ObjectString"
},
{
"someInteger": 99999,
"someString": "ObjectString"
}
],
"someObject": {
"someInteger": 99999,
"someString": "ObjectString"
},
"strings": [
"list of strings with index 0",
"list of strings with index 1",
"list of strings with index 2"
]
}
— , XML , toJson toXml.
example.
محددات
الحقول الفردية
يجب أن توفر الأنواع الأولية أو المعرفة من قبل المستخدم مُنشئًا افتراضيًا.
المجموعات
يجب أن يكون صنف المجموعة مقولبًا وأن يوفر طرقًا واضحة ، بحجم ، وإلحاق. يمكنك استخدام مجموعاتك الخاصة ، وفقًا للشروط. مجموعات Qt التي تفي بهذه الشروط: QVector و QStack و QList و QQueue.
إصدارات Qt
الإصدار الأدنى Qt 5.5.0
الحد الأدنى للإصدار الذي تم اختباره Qt 5.9.0
الحد الأقصى للإصدار الذي تم اختباره Qt 5.15.0
ملاحظة: يمكنك المشاركة في اختبار واختبار QSerializer على الإصدارات السابقة من Qt
النتيجة
عند إعادة صياغة QSerializer ، لم أضع نفسي مطلقًا في مهمة تقليله بشكل كبير. ومع ذلك ، انخفض حجمه من 9 ملفات إلى 1 ، مما قلل أيضًا من تعقيده. الآن لم يعد QSerializer مكتبة في شكلنا المعتاد ، فهو الآن مجرد ملف رأس ، وهو ما يكفي لتضمينه في المشروع والحصول على جميع الوظائف للتسلسل / إلغاء التسلسل المريح. بدأ التطوير مرة أخرى في شهر مارس ، حيث تم اختراع بنية ذكية وتضخم المشروع مع التبعيات والعكازات ، وأعيد كتابتها من 0 عدة مرات. وكل ذلك من أجل أن يتحول في النهاية إلى ملف صغير.
أسأل نفسي: "هل كان يستحق الجهد المبذول فيه؟" ، أجبت: "نعم ، كان كذلك. لقد جربتها بالفعل في مشاريعي القتالية والنتيجة أسعدتني.
روابط
جيثب: رابط
أحدث إصدار: v1.1
المقال السابق: QSerializer: حل بسيط لتسلسل JSON / XML
قائمة المستقبل
- انخفاض كبير في التكلفة (يمكن القيام به بتكلفة أقل)
- الاكتناز
- العمل مع الأنواع المهمة
- الوصف الأساسي للبيانات القابلة للتسلسل
- دعم أي مجموعة نموذجية توفر طرقًا واضحة ، بحجم ، وإلحاق. حتى الخاصة بهم
- مجموعات قابلة للتغيير بالكامل عند إلغاء التسلسل
- دعم لجميع الأنواع البدائية الشعبية
- دعم أي نوع مخصص موصوف باستخدام QSerializer
- لا حاجة لتسجيل الأنواع المخصصة