الماكرو المتغير

الماكرو المتغير هو ميزة لبعض لغات برمجة الكمبيوتر ، وخاصة المعالج المسبق للغة C ، حيث يمكن تعريف الماكرو لقبول عدد متغير من الوسائط .

تم تقديم وحدات الماكرو ذات الوسائط المتغيرة في عام 1999 في مراجعة ISO/IEC 9899:1999 ( C99 ) لمعيار لغة C ، وفي عام 2011 في مراجعة ISO/IEC 14882:2011 ( C++11 ) لمعيار لغة C++ . [ 1 ] أُضيف دعم وحدات الماكرو ذات الوسائط المتغيرة بدون وسائط في C++20 و C++23 . [ 2 ] [ 3 ]

لغة C/C++

صيغة الإعلان

صيغة التصريح مشابهة لصيغة الدوال ذات عدد الوسائط المتغير : حيث تُستخدم سلسلة من ثلاث نقاط " ..." للإشارة إلى ضرورة تمرير وسيط واحد أو أكثر. أثناء توسيع الماكرو، يتم استبدال كل ظهور للمعرف الخاص __VA_ARGS__في قائمة استبدال الماكرو بالوسائط المُمرَّرة.

بالإضافة إلى ذلك، يمكن إدراج وسائط الماكرو العادية قبل ...، [ 4 ] ولكن لا يجوز إدراج الوسائط العادية بعد ....

لا توجد وسيلة للوصول إلى الوسائط الفردية في قائمة الوسائط المتغيرة، ولا لمعرفة عدد الوسائط التي تم تمريرها. ومع ذلك، يمكن كتابة وحدات ماكرو لحساب عدد الوسائط التي تم تمريرها. [ 5 ]

يتطلب كل من معياري C99 و C++11 وسيطًا واحدًا على الأقل، ولكن منذ C++20 و C23 ، تم رفع هذا القيد باستخدام __VA_OPT__الماكرو الوظيفي. __VA_OPT__يُستبدل الماكرو بالوسيط الخاص به عند وجود وسائط، ويُحذف في غير ذلك. مع ذلك، تسمح المترجمات الشائعة أيضًا بتمرير صفر من الوسائط قبل هذه الإضافة. [ 4 ] [ 6 ]

تمنع قواعد المعالجة المسبقة للغة C توسيع أسماء الماكرو في وسيط __VA_OPT__الدالة بشكل متكرر. ومع ذلك، من الممكن تجاوز هذا القيد حتى عدد ثابت من عمليات التوسيع المتكررة. [ 7 ]

يدعم

تدعم العديد من المترجمات وحدات الماكرو ذات الوسائط المتغيرة عند ترجمة أكواد C وC++، ومنها: مجموعة مترجمات GNU 3.0، [ 4 ] وClang (جميع الإصدارات)، [ 8 ] و Visual Studio 2005 ، [ 6 ] و C++Builder 2006، و Oracle Solaris Studio (المعروف سابقًا باسم Sun Studio) Forte Developer 6 update 2 (إصدار C++ 5.3). [ 9 ] كما يدعم GCC وحدات الماكرو هذه عند ترجمة Objective-C .

تمت إضافة دعم الماكرو __VA_OPT__لدعم الوسائط الصفرية في GNU Compiler Collection[ 10 ] Clang[ 11 ] و Visual Studio 2019. [ 12 ]

مثال

إذا كانت هناك حاجة إلى دالةprintf مشابهة ، تأخذ رقم الملف ورقم السطر الذي تم استدعاؤها منه كوسيطات، فإن الحل التالي ينطبق.dbgprintf()

الوظيفة التي قمنا بتنفيذها:

void debugPrint ( const char * file , int line , const char * fmt , ...);

نظراً لقيود دعم وحدات الماكرو المتغيرة في C++11، فإن الحل المباشر التالي قد يفشل، وبالتالي يجب تجنبه:

#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat, __VA_ARGS__)

والسبب هو أن

dbgprintf ( "Hallo" )

يتم توسيعه إلى

debugPrint ( __FILE__ , __LINE__ , "Hallo" , )

حيث أن وجود الفاصلة قبل القوس المغلق سيؤدي إلى خطأ في بناء الجملة.

يدعم GNU C++ امتدادًا غير قابل للنقل يحل هذه المشكلة:

#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat, ##__VA_ARGS__)

يدعم C++20 الصيغة التالية.

#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)

باستخدام cformatالسلسلة كجزء من الوسائط المتغيرة، يمكننا تجاوز حالات عدم التوافق المذكورة أعلاه. هذا الأمر معقد ولكنه قابل للتطبيق على نطاق واسع.

#define dbgprintf(...) debugPrint(__FILE__, __LINE__, __VA_ARGS__)

dbgprintf()ويمكن تسميتها حينها بـ

dbgprintf ( "Hello, world" );

والذي يتوسع إلى

debugPrint ( __FILE__ , __LINE__ , "Hello, world" );

مثال آخر هو:

dbgprintf ( "%d + %d = %d" , 2 , 2 , 5 );

والذي يتوسع إلى

debugPrint ( __FILE__ , __LINE__ , "%d + %d = %d" , 2 , 2 , 5 );

بدون استخدام وحدات الماكرو المتغيرة، لا يمكن كتابة أغلفة printfمباشرة. الحل البديل الشائع هو استخدام وظيفة stdargs في لغتي C/C++، وجعل الدالة تستدعي الدالة vprintfبدلاً من ذلك.

الفاصلة الأخيرة

توجد مشكلة في قابلية النقل تتعلق بإنشاء فاصلة زائدة في نهاية الوسائط الفارغة لوحدات الماكرو المتغيرة في لغة C99 . بعض المترجمات (مثل Visual Studio عند عدم استخدام المعالج المسبق المتوافق مع المعيار الجديد [ 6 ] ) تحذف الفاصلة الزائدة تلقائيًا. بينما تدعم مترجمات أخرى (مثل GCC [ 4 ] ) وضعها ##قبل الفاصلة __VA_ARGS__.

#define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)

يعمل التطبيق التالي

MYLOG ( "عدد كبير جدًا من البالونات %u" , 42 );

والذي يتوسع إلى

fprintf ( stderr , "%s(%u): " "عدد كبير جدًا من البالونات %u" " \n " , __FILE__ , __LINE__ , 42 );

وهو ما يعادل

fprintf ( stderr , "%s(%u): Too many balloons %u \n " , __FILE__ , __LINE__ , 42 );

لكن انظر إلى هذا التطبيق:

سجلّي ( "انتباه!" );

والذي يتوسع إلى

fprintf ( stderr , "%s(%u): " "انتباه!" " \n " , __FILE__ , __LINE__ , );

مما يؤدي إلى حدوث خطأ في بناء الجملة مع GCC.

يدعم GCC الامتداد التالي (غير القابل للنقل):

#define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)

والذي يزيل الفاصلة الأخيرة عندما __VA_ARGS__يكون فارغًا.

يحل C23 هذه المشكلة من خلال تقديم __VA_OPT__ما يشبه C++. [ 3 ]

البدائل

قبل وجود الوسائط المتغيرة في لغة C99، كان من الشائع جدًا استخدام الأقواس المتداخلة المزدوجة لاستغلال العدد المتغير من الوسائط التي يمكن توفيرها للدالة printf():

#define dbgprintf(x) debugPrint x

dbgprintf()ويمكن تسميتها حينها على النحو التالي:

dbgprintf (( "Hello, world %d" , 27 ));

والذي يتوسع إلى:

debugPrint ( "Hello, world %d" , 27 );

الصدأ

في لغة Rust ، تسمح الواجهة المتغيرة الوسائط (المعروفة أيضًا باسم الماكرو المتغير الوسائط) [ 13 ] بأخذ عدد غير محدود من الوسائط. تفتقر Rust إلى الدوال والقوالب المتغيرة الوسائط ، لذا فإن الطريقة الوحيدة لتحقيق ذلك هي من خلال هذه الواجهات، التي تستخدم أنماط التكرار macro_rules!. على سبيل المثال، في ، يرمز إلى نمط متكرر، ويرمز إلى فاصل، ويرمز إلى صفر أو أكثر من التكرارات ( نجمة كلين ، بينما يرمز الذي يطابق تكرارًا واحدًا أو أكثر إلى زائد كلين ).$(...),*$(),*+

أحد الأمثلة على تطبيق ماكرو متغير الوسائط هو التالي، وهو مكافئ لما يلي vec!:

// تعريف ماكرو باسم "make_vec!" macro_rules! make_vec { // يطابق أي تعبير Rust // ويكرر النمط مفصولًا بفواصل، صفرًا أو أكثر من المرات ( $( $x : expr ), * ) => { { // يجب أن يكون Vec مؤهلًا بالكامل // لمنع حله إلى Vec معرف من قبل المستخدم let mut temp = :: std :: vec :: Vec :: new (); // تكرار عملية الدفع إلى temp مرة واحدة لكل وسيط $( temp.push ( $ x ); ) * // إرجاع temp من الماكرو temp } }; }

مراجع

  1. نيلسون، كلارك (16 يوليو 2004). "مسودة تغييرات العمل لمزامنة المعالج المسبق C99" . www.open-std.org . تاريخ الاسترجاع: 30 أكتوبر 2025 .
  2. "حذف الفاصلة وحذف الفاصلة" . 18 يونيو 2017. تم الاطلاع عليه في 24 ديسمبر 2022 .
  3. 1 2 "WG14 - N3033 : حذف الفاصلة وحذف الفاصلة" 2022-07-20. 
  4. 1 2 3 4 وحدات الماكرو المتغيرة - استخدام مجموعة مترجمات GNU (GCC)
  5. ^ لوران دينياو (2006-01-16). "__VA_NARG__" . مجموعة الأخبار : comp.std.c . يوزنت: dqgm2f$ije$1@sunnews.cern.ch .  
  6. 1 2 3 وحدات الماكرو المتغيرة (C++)
  7. وحدات الماكرو التكرارية مع C++20 __VA_OPT__
  8. تغيير في شفرة مصدر Clang يشير إلى دعم __VA_ARGS__ (29-07-2006)، مع العلم أن Clang أصبح مفتوح المصدر في عام 2007. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770
  9. مقارنة ميزات Sun Studio – http://developers.sun.com/sunstudio/support/CCcompare.html
  10. "دعم C++2a في GCC" . تم الاطلاع عليه بتاريخ 14 يونيو 2018 .
  11. "دعم لغة C++ في Clang" . تم الاطلاع عليه بتاريخ 14 يونيو 2018 .
  12. "نظرة عامة على المعالج المسبق الجديد لـ MSVC" . 10 سبتمبر 2020. تم الاطلاع عليه في 8 ديسمبر 2020 .
  13. فريق Rust (1 أبريل 2026). "واجهات متغيرة" . rust-lang.org . Rust بالأمثلة.

انظر أيضاً