تشير اتجاهات تطوير C ++ الحديثة إلى الحد الأقصى المحتمل لرفض وحدات الماكرو في التعليمات البرمجية. لكن في بعض الأحيان بدون وحدات ماكرو ، وفي مظاهرها القبيحة بشكل خاص ، لا يمكن للمرء فعل ذلك ، لأنه بدونها يكون الأمر أسوأ. عن هذا والقصة.
كما تعلم ، فإن الخطوة الأولى في التحويل البرمجي C و C ++ هي المعالج المسبق ، الذي يحل محل وحدات الماكرو وتوجيهات المعالج المسبق بنص عادي.
هذا يتيح لنا القيام بأشياء غريبة ، على سبيل المثال ، مثل:
بعد عمل المعالج السابق ، سوف يتحول سوء الفهم هذا إلى الكود الصحيح:
std::string str = "look, I'm a string!" ;
بالطبع ، لا يمكن إدراج هذا العنوان الرهيب في أي مكان آخر. ونعم ، نظرًا لحقيقة أننا سنضيف هذا العنوان عدة مرات إلى نفس الملف - لا # براغما مرة واحدة أو تشمل الحراس.
في الواقع ، دعنا نكتب مثالًا أكثر تعقيدًا من شأنه أن يفعل أشياء مختلفة بمساعدة وحدات الماكرو وفي الوقت نفسه ، دافع ضد # عشوائي يشمل:
هذا لا يزال قبيحًا ، لكن سحرًا معينًا يظهر بالفعل: عندما تضيف عنصرًا جديدًا إلى فئة التعداد ، ستتم إضافته تلقائيًا إلى بيان الإخراج الزائد.
هنا يمكنك إضفاء الطابع الرسمي على مجال تطبيق هذه الطريقة: الحاجة إلى إنشاء الشفرة في أماكن مختلفة بناءً على مصدر واحد.
والآن قصة حزينة من X- ماكرو وويندوز. يوجد مثل نظام Windows عدادات الأداء ، والذي يسمح لك بإرسال عدادات معينة إلى نظام التشغيل حتى تتمكن التطبيقات الأخرى من التقاطها. على سبيل المثال ، يمكن تكوين Zabbix لجمع ومراقبة أي عداد أداء. هذا مناسب تمامًا ، ولا تحتاج إلى إعادة اختراع العجلة بإرجاع / استعلام البيانات.
اعتقدت بصدق أن إضافة عداد جديد يبدو أنه عداد HANDLE = AddCounter ("name"). آه ، كم كنت مخطئا.
تحتاج أولاً إلى كتابة ملف XML خاص (
مثال ) ، أو إنشائه باستخدام برنامج ecmangen.exe من Windows SDK ، لكن
لسبب ما تمت إزالة ecmangen من الإصدارات الجديدة من Windows 10 SDK. بعد ذلك ، تحتاج إلى إنشاء الكود وملف .rc باستخدام الأداة المساعدة
ctrpp بناءً على بيان XML الخاص بنا.
تتم إضافة عدادات جديدة إلى النظام نفسه فقط مع الأداة المساعدة
lodctr مع بيان XML في الوسيطة.
ما هو ملف .rc؟هذا اختراع من Microsoft ، لا يتعلق بـ C ++ القياسي. باستخدام هذه الملفات ، يمكنك تضمين الموارد في exe \ dll ، مثل السلاسل \ الرموز / الصور ، إلخ ، ثم التقاطها باستخدام واجهة برمجة تطبيقات Windows الخاصة.
يستخدم Perfcounters هذه الملفات .rc لتوطين أسماء العدادات ، وليس من الواضح جدًا لماذا يجب ترجمة هذه الأسماء.
تلخيص ما ورد أعلاه: لإضافة عداد واحد تحتاجه:
- تغيير ملف XML الظاهر
- إنشاء ملفات مشروع .c و .rc جديدة بناءً على البيان
- اكتب وظيفة جديدة ستزيد عداد جديد
- اكتب وظيفة جديدة ستأخذ قيمة العداد
المجموع: 4-5 ملفات معدلة في فرق من أجل عداد واحد ومعاناة مستمرة من العمل مع بيان XML ، والذي هو مصدر المعلومات في كود الجمع. هذا ما تقدمه لنا Microsoft.
في الواقع ، يبدو الحل المبتكر مخيفًا ، ولكن يتم إضافة عداد جديد بسطر واحد في ملف واحد تمامًا. علاوة على ذلك ، يتم إنشاء كل شيء تلقائيًا باستخدام وحدات الماكرو ، وللأسف ، برنامج نصي مسبق الإنشاء ، نظرًا لأن بيان XML لا يزال مطلوبًا ، على الرغم من أنه ليس الآن هو الملف الرئيسي.
يبدو perfcounters_ctr.h متطابقًا تقريبًا مع المثال أعلاه:
#ifndef NV_PERFCOUNTER #error "You cannot do this!" #endif ... NV_PERFCOUNTER(copied_bytes) NV_PERFCOUNTER(copied_files) ... #undef NV_PERFCOUNTER
كما كتبت في وقت سابق ، تتم إضافة عدادات عن طريق تحميل ملف تعريف XML باستخدام lodctr.exe. من برنامجنا ، يمكننا فقط تهيئة وتعديلها.
تبدو أجزاء التهيئة التي تهمنا في المباراة الافتتاحية كما يلي:
#define COPIED_BYTES 0
المجموع: نحتاج إلى مراسلة للنموذج "فهرس زيادة اسم العداد" ، وفي مرحلة التجميع ، من الضروري معرفة عدد العدادات وجمع صفيف تهيئة من مؤشرات العدادات. هذا هو المكان الذي يأتي فيه الماكرو X في عملية الإنقاذ.
مطابقة الاسم المضاد لمؤشره الصاعد أمر بسيط للغاية.
ستتحول الكود الموجود أدناه إلى فئة التعداد ، والتي تبدأ فهارسها الداخلية عند 0 ، وتزيد بمقدار واحد. بإضافة العنصر الأخير بأيدينا ، فإننا نكتشف على الفور عدد العدادات الكلية التي لدينا:
enum class counter_enum : int { #define NV_PERFCOUNTER(ctr) ctr, #include "perfcounters_ctr.h" total_counters };
علاوة على ذلك ، استنادًا إلى التعداد لدينا ، نحتاج إلى تهيئة العدادات:
static constexpr int counter_count = static_cast<int>(counter_enum::total_counters); const PERF_COUNTERSET_INFO counterset_info{ ... counter_count, ... }; struct { PERF_COUNTERSET_INFO set; PERF_COUNTER_INFO counters[counter_count]; } counterset { counterset_info, {
وكانت النتيجة أن تهيئة العداد الجديد تأخذ الآن سطرًا واحدًا ولا تتطلب تغييرات إضافية في الملفات الأخرى (سابقًا ، قام كل تجديد بتغيير 3 أجزاء من التعليمات البرمجية في التهيئة فقط).
ودعنا نضيف واجهة برمجة تطبيقات ملائمة لزيادة العدادات. شيء في الروح:
#define NV_PERFCOUNTER(ctr) \ inline void ctr##_tick(size_t diff = 1) { } #include "perfcounters_ctr.h" #define NV_PERFCOUNTER(ctr) \ inline size_t ctr##_get() { } #include "perfcounter_ctr.h"
سيقوم المعالج المسبق بإنشاء مجموعات / أدوات تسوية جميلة لنا ، والتي يمكننا استخدامها على الفور في الكود ، على سبيل المثال:
inline void copied_bytes_tick(size_t diff = 1); inline size_t copied_bytes_get();
لكن لا يزال أمامنا شيئين حزينين: ملف XML وملف .rc (للأسف ، من الضروري).
لقد جعلنا الأمر بسيطًا بما يكفي - برنامج نصي تم إنشاؤه مسبقًا يقوم بقراءة الملف الأصلي مع وحدات الماكرو التي تحدد العدادات ، ويوزع ما بين "NV_COUNTER (" و ")" ، وبناءً على ذلك ، فإنه ينشئ كلا الملفين الموجودان في .gitignore لا فرق القمامة.
كان : برنامج خاص يعتمد على كود الترميز الذي تم إنشاؤه في XML. الكثير من التغييرات في المشروع لكل إضافة / إزالة العداد.
الآن: يقوم البرنامج النصي preprocessor و prebuild بإنشاء جميع العدادات وبيان XML وملف .rc. بالضبط سطر واحد في الفرق لإضافة / إزالة عداد. بفضل المعالج الأولي الذي ساعد في حل هذه المشكلة ، أظهر في هذه الحالة بالذات أكثر من ضرر.