سؤال

ال ج يخشى مجتمع C++ المعالج المسبق ويتجنبه بشكل مبرر.عادةً ما تكون الوظائف المضمنة والثوابت والقوالب بديلاً أكثر أمانًا ومتفوقًا لـ #define.

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

#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)  

لا يتفوق بأي حال من الأحوال على النوع الآمن:

inline bool succeeded(int hr) { return hr >= 0; }

لكن وحدات الماكرو لها مكانها، يرجى إدراج الاستخدامات التي تجدها لوحدات الماكرو التي تستخدمها لا أستطيع الاستغناء عن المعالج المسبق.

يرجى وضع كل حالات الاستخدام في إجابة منفصلة حتى يمكن التصويت عليها وإذا كنت تعرف كيفية تحقيق إحدى الإجابات دون أن يشير الأستاذ إلى كيفية ذلك في تعليقات تلك الإجابة.

هل كانت مفيدة؟

المحلول

كمغلفات لوظائف تصحيح الأخطاء، لتمرير أشياء مثل __FILE__, __LINE__, ، إلخ:

#ifdef ( DEBUG )
#define M_DebugLog( msg )  std::cout << __FILE__ << ":" << __LINE__ << ": " << msg
#else
#define M_DebugLog( msg )
#endif

نصائح أخرى

يجب أن تكون الطرق دائمًا تعليمات برمجية كاملة وقابلة للترجمة؛قد تكون وحدات الماكرو عبارة عن أجزاء من التعليمات البرمجية.وبالتالي يمكنك تحديد ماكرو foreach:

#define foreach(list, index) for(index = 0; index < list.size(); index++)

واستخدامها على النحو التالي:

foreach(cookies, i)
    printf("Cookie: %s", cookies[i]);

منذ C++ 11، تم استبدال هذا بـ على أساس النطاق للحلقة.

حراس ملف الرأس يستلزم وحدات الماكرو.

هل هناك أي مجالات أخرى ذلك يستلزم وحدات الماكرو؟ليس كثيرًا (إن وجد).

هل هناك أي حالات أخرى تستفيد من وحدات الماكرو؟نعم!!!

أحد الأماكن التي أستخدم فيها وحدات الماكرو هو التعليمات البرمجية المتكررة جدًا.على سبيل المثال، عند تغليف تعليمات برمجية C++ لاستخدامها مع واجهات أخرى (.NET، COM، Python، إلخ...)، أحتاج إلى اكتشاف أنواع مختلفة من الاستثناءات.وإليك كيف أفعل ذلك:

#define HANDLE_EXCEPTIONS \
catch (::mylib::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e); \
} \
catch (::std::exception& e) { \
    throw gcnew MyDotNetLib::Exception(e, __LINE__, __FILE__); \
} \
catch (...) { \
    throw gcnew MyDotNetLib::UnknownException(__LINE__, __FILE__); \
}

لا بد لي من وضع هذه المصيد في كل وظيفة مجمعة.بدلاً من كتابة كتل الالتقاط الكاملة في كل مرة، أكتب فقط:

void Foo()
{
    try {
        ::mylib::Foo()
    }
    HANDLE_EXCEPTIONS
}

وهذا أيضًا يجعل الصيانة أسهل.إذا اضطررت إلى إضافة نوع استثناء جديد، فهناك مكان واحد فقط أحتاج إلى إضافته.

هناك أمثلة أخرى مفيدة أيضًا:العديد منها تشمل __FILE__ و __LINE__ وحدات ماكرو المعالج.

على أية حال، وحدات الماكرو مفيدة جدًا عند استخدامها بشكل صحيح.وحدات الماكرو ليست شريرة - فهي سوء استخدام شر.

خاصة:

  1. تشمل الحراس
  2. التجميع الشرطي
  3. إعداد التقارير (وحدات الماكرو المحددة مسبقًا مثل __LINE__ و __FILE__)
  4. (نادرًا) تكرار أنماط التعليمات البرمجية المتكررة.
  5. في كود منافسك.

داخل الترجمة الشرطية، للتغلب على مشكلات الاختلافات بين المترجمين:

#ifdef ARE_WE_ON_WIN32
#define close(parm1)            _close (parm1)
#define rmdir(parm1)            _rmdir (parm1)
#define mkdir(parm1, parm2)     _mkdir (parm1)
#define access(parm1, parm2)    _access(parm1, parm2)
#define create(parm1, parm2)    _creat (parm1, parm2)
#define unlink(parm1)           _unlink(parm1)
#endif

عندما تريد إنشاء سلسلة من التعبير، فإن أفضل مثال على ذلك هو assert (#x تتحول قيمة x إلى سلسلة).

#define ASSERT_THROW(condition) \
if (!(condition)) \
     throw std::exception(#condition " is false");

في بعض الأحيان يتم تعريف ثوابت السلسلة بشكل أفضل على أنها وحدات ماكرو حيث يمكنك القيام بالمزيد باستخدام سلسلة حرفية أكثر من استخدام a const char *.

على سبيل المثاليمكن أن تكون سلسلة حرفية متسلسلة بسهولة.

#define BASE_HKEY "Software\\Microsoft\\Internet Explorer\\"
// Now we can concat with other literals
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "Settings", &settings);
RegOpenKey(HKEY_CURRENT_USER, BASE_HKEY "TypedURLs", &URLs);

اذا كان const char * تم استخدام نوع ما من فئات السلسلة لإجراء التسلسل في وقت التشغيل:

const char* BaseHkey = "Software\\Microsoft\\Internet Explorer\\";
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "Settings").c_str(), &settings);
RegOpenKey(HKEY_CURRENT_USER, (string(BaseHkey) + "TypedURLs").c_str(), &URLs);

عندما تريد تغيير تدفق البرنامج (return, break و continue) يتصرف الكود الموجود في الدالة بشكل مختلف عن الكود المضمن بالفعل في الدالة.

#define ASSERT_RETURN(condition, ret_val) \
if (!(condition)) { \
    assert(false && #condition); \
    return ret_val; }

// should really be in a do { } while(false) but that's another discussion.

الواضح يشمل الحراس

#ifndef MYHEADER_H
#define MYHEADER_H

...

#endif

لا يمكنك إجراء ماس كهربائى لوسيطات استدعاء الوظيفة باستخدام استدعاء دالة عادي.على سبيل المثال:

#define andm(a, b) (a) && (b)

bool andf(bool a, bool b) { return a && b; }

andm(x, y) // short circuits the operator so if x is false, y would not be evaluated
andf(x, y) // y will always be evaluated

لنفترض أننا سنتجاهل الأشياء الواضحة مثل حراس الرأس.

في بعض الأحيان، قد ترغب في إنشاء تعليمات برمجية تحتاج إلى النسخ/اللصق بواسطة المترجم المسبق:

#define RAISE_ERROR_STL(p_strMessage)                                          \
do                                                                             \
{                                                                              \
   try                                                                         \
   {                                                                           \
      std::tstringstream strBuffer ;                                           \
      strBuffer << p_strMessage ;                                              \
      strMessage = strBuffer.str() ;                                           \
      raiseSomeAlert(__FILE__, __FUNCSIG__, __LINE__, strBuffer.str().c_str()) \
   }                                                                           \
   catch(...){}                                                                \
   {                                                                           \
   }                                                                           \
}                                                                              \
while(false)

والتي تمكنك من ترميز هذا:

RAISE_ERROR_STL("Hello... The following values " << i << " and " << j << " are wrong") ;

ويمكن أن تولد رسائل مثل:

Error Raised:
====================================
File : MyFile.cpp, line 225
Function : MyFunction(int, double)
Message : "Hello... The following values 23 and 12 are wrong"

لاحظ أن خلط القوالب مع وحدات الماكرو يمكن أن يؤدي إلى نتائج أفضل (على سبيل المثال.إنشاء القيم تلقائيًا جنبًا إلى جنب مع أسماء المتغيرات الخاصة بها)

وفي أحيان أخرى، تحتاج إلى __FILE__ و/أو __LINE__ لبعض التعليمات البرمجية، لإنشاء معلومات التصحيح، على سبيل المثال.ما يلي كلاسيكي لـ Visual C++:

#define WRNG_PRIVATE_STR2(z) #z
#define WRNG_PRIVATE_STR1(x) WRNG_PRIVATE_STR2(x)
#define WRNG __FILE__ "("WRNG_PRIVATE_STR1(__LINE__)") : ------------ : "

كما هو الحال مع الكود التالي:

#pragma message(WRNG "Hello World")

يولد رسائل مثل:

C:\my_project\my_cpp_file.cpp (225) : ------------ Hello World

وفي أحيان أخرى، تحتاج إلى إنشاء تعليمات برمجية باستخدام عاملي التسلسل # و##، مثل إنشاء الحروف والمحددات لخاصية ما (هذا في حالات محدودة جدًا).

وفي أحيان أخرى، سوف تقوم بإنشاء تعليمات برمجية لن يتم تجميعها إذا تم استخدامها من خلال وظيفة، مثل:

#define MY_TRY      try{
#define MY_CATCH    } catch(...) {
#define MY_END_TRY  }

والتي يمكن استخدامها ك

MY_TRY
   doSomethingDangerous() ;
MY_CATCH
   tryToRecoverEvenWithoutMeaningfullInfo() ;
   damnThoseMacros() ;
MY_END_TRY

(ما زلت أرى فقط هذا النوع من التعليمات البرمجية يستخدم بشكل صحيح مرة واحدة)

وأخيرًا وليس آخرًا المشهور boost::foreach !!!

#include <string>
#include <iostream>
#include <boost/foreach.hpp>

int main()
{
    std::string hello( "Hello, world!" );

    BOOST_FOREACH( char ch, hello )
    {
        std::cout << ch;
    }

    return 0;
}

(ملحوظة:نسخ/لصق الكود من الصفحة الرئيسية للتعزيز)

وهو (IMHO) أفضل بكثير من std::for_each.

لذا، تكون وحدات الماكرو مفيدة دائمًا لأنها خارج قواعد المترجم العادية.لكنني أجد أنه في أغلب الأحيان أرى واحدة منها، فهي في الواقع بقايا من كود C لم تتم ترجمتها إلى لغة C++ مناسبة.

أطر اختبار الوحدة لـ C++ مثل UnitTest++ تدور إلى حد كبير حول وحدات الماكرو للمعالج المسبق.تتوسع بضعة أسطر من كود اختبار الوحدة إلى تسلسل هرمي للفئات التي لن يكون من الممتع كتابتها يدويًا على الإطلاق.بدون شيء مثل UnitTest++ وهو سحر المعالج المسبق، لا أعرف كيف يمكنك كتابة اختبارات الوحدة بكفاءة لـ C++.

إن الخوف من المعالج الأولي C يشبه الخوف من المصابيح المتوهجة لمجرد حصولنا على مصابيح الفلورسنت.نعم ، يمكن أن يكون الأول {كهرباء | وقت مبرمج} غير فعال.نعم، يمكنك أن تحرقهم (حرفيًا).لكن يمكنهم إنجاز المهمة إذا تعاملت معها بشكل صحيح.

عندما تقوم ببرمجة الأنظمة المضمنة، تستخدم لغة C لتكون الخيار الوحيد بعيدًا عن مُجمِّع النماذج.بعد البرمجة على سطح المكتب باستخدام C++ ثم التبديل إلى أهداف أصغر ومضمنة، تتعلم التوقف عن القلق بشأن "عدم التجانس" للعديد من ميزات C العارية (بما في ذلك وحدات الماكرو) ومحاولة اكتشاف الاستخدام الأفضل والآمن الذي يمكنك الحصول عليه من تلك سمات.

الكسندر ستيبانوف يقول:

عندما نبرمج في C ++ ، لا ينبغي أن نخجل من تأريخها ، ولكن نستفيد منها بالكامل.المشاكل الوحيدة مع C ++ ، وحتى المشاكل الوحيدة مع C ، تنشأ عندما لا يتسق هم أنفسهم مع منطقهم.

نحن نستخدم ال __FILE__ و __LINE__ وحدات الماكرو لأغراض التشخيص في طرح الاستثناءات الغنية بالمعلومات والتقاطها وتسجيلها، جنبًا إلى جنب مع الماسحات الضوئية الآلية لملفات السجل في البنية التحتية لضمان الجودة لدينا.

على سبيل المثال، رمي ماكرو OUR_OWN_THROW يمكن استخدامه مع نوع الاستثناء ومعلمات المُنشئ لهذا الاستثناء، بما في ذلك الوصف النصي.مثله:

OUR_OWN_THROW(InvalidOperationException, (L"Uninitialized foo!"));

سيؤدي هذا الماكرو بالطبع إلى رمي InvalidOperationException استثناء مع الوصف كمعلمة منشئة، ولكنها ستكتب أيضًا رسالة إلى ملف سجل يتكون من اسم الملف ورقم السطر الذي حدث فيه الرمي ووصفه النصي.سيحصل الاستثناء الذي تم طرحه على معرف، والذي يتم تسجيله أيضًا.إذا تم اكتشاف الاستثناء في مكان آخر في الكود، فسيتم وضع علامة عليه على هذا النحو وسيشير ملف السجل بعد ذلك إلى أن هذا الاستثناء المحدد قد تمت معالجته، وبالتالي فمن غير المحتمل أن يكون سبب أي عطل قد يتم تسجيله لاحقًا.يمكن التقاط الاستثناءات التي لم تتم معالجتها بسهولة من خلال البنية التحتية الآلية لضمان الجودة لدينا.

تكرار الكود.

إلقاء نظرة على تعزيز مكتبة المعالجات المسبقة, ، إنه نوع من البرمجة الفوقية.في الموضوع->الدافع يمكنك العثور على مثال جيد.

لا يزال من الممكن إنشاء بعض الأشياء المتقدمة والمفيدة للغاية باستخدام المعالج المسبق (وحدات الماكرو)، وهو ما لن تتمكن أبدًا من القيام به باستخدام "بنيات اللغة" الخاصة بـ c++ بما في ذلك القوالب.

أمثلة:

جعل شيء ما معرف C وسلسلة

طريقة سهلة لاستخدام متغيرات أنواع التعداد كسلسلة في C

تعزيز البرمجة الفوقية للمعالج المسبق

أحيانًا أستخدم وحدات الماكرو حتى أتمكن من تحديد المعلومات في مكان واحد، ولكن استخدامها بطرق مختلفة في أجزاء مختلفة من الكود.إنه شرير قليلاً فقط :)

على سبيل المثال، في "field_list.h":

/*
 * List of fields, names and values.
 */
FIELD(EXAMPLE1, "first example", 10)
FIELD(EXAMPLE2, "second example", 96)
FIELD(ANOTHER, "more stuff", 32)
...
#undef FIELD

ثم بالنسبة للتعداد العام يمكن تعريفه باستخدام الاسم فقط:

#define FIELD(name, desc, value) FIELD_ ## name,

typedef field_ {

#include "field_list.h"

    FIELD_MAX

} field_en;

وفي دالة init خاصة، يمكن استخدام جميع الحقول لملء جدول بالبيانات:

#define FIELD(name, desc, value) \
    table[FIELD_ ## name].desc = desc; \
    table[FIELD_ ## name].value = value;

#include "field_list.h"

أحد الاستخدامات الشائعة هو اكتشاف بيئة الترجمة، ومن أجل التطوير عبر الأنظمة الأساسية، يمكنك كتابة مجموعة واحدة من التعليمات البرمجية لنظام التشغيل Linux، على سبيل المثال، ومجموعة أخرى لنظام التشغيل Windows عندما لا توجد مكتبة مشتركة بالفعل لأغراضك.

لذلك، في مثال تقريبي يمكن أن يكون هناك كائن مزامنة عبر الأنظمة الأساسية

void lock()
{
    #ifdef WIN32
    EnterCriticalSection(...)
    #endif
    #ifdef POSIX
    pthread_mutex_lock(...)
    #endif
}

بالنسبة للوظائف، فهي مفيدة عندما تريد تجاهل أمان الكتابة بشكل صريح.مثل الأمثلة العديدة المذكورة أعلاه وأدناه للقيام بـ ASSERT.بالطبع، مثل الكثير من ميزات C/C++، يمكنك إطلاق النار على نفسك، لكن اللغة تمنحك الأدوات وتتيح لك تحديد ما يجب القيام به.

شيء مثل

void debugAssert(bool val, const char* file, int lineNumber);
#define assert(x) debugAssert(x,__FILE__,__LINE__);

بحيث يمكنك فقط على سبيل المثال

assert(n == true);

واحصل على اسم الملف المصدر ورقم السطر الخاص بالمشكلة مطبوعًا على السجل الخاص بك إذا كانت n خاطئة.

إذا كنت تستخدم استدعاء دالة عادية مثل

void assert(bool val);

بدلاً من الماكرو، كل ما يمكنك الحصول عليه هو رقم السطر الخاص بوظيفة التأكيد المطبوع على السجل، والذي سيكون أقل فائدة.

#define ARRAY_SIZE(arr) (sizeof arr / sizeof arr[0])

على عكس حل القالب "المفضل" الذي تمت مناقشته في الموضوع الحالي، يمكنك استخدامه كتعبير ثابت:

char src[23];
int dest[ARRAY_SIZE(src)];

يمكنك استخدام #defines للمساعدة في تصحيح الأخطاء وسيناريوهات اختبار الوحدة.على سبيل المثال، قم بإنشاء متغيرات تسجيل خاصة لوظائف الذاكرة وقم بإنشاء memlog_preinclude.h خاص:

#define malloc memlog_malloc
#define calloc memlog calloc
#define free memlog_free

قم بتجميع التعليمات البرمجية الخاصة بك باستخدام:

gcc -Imemlog_preinclude.h ...

رابط في memlog.o الخاص بك إلى الصورة النهائية.أنت الآن تتحكم في malloc، وما إلى ذلك، ربما لأغراض التسجيل، أو لمحاكاة فشل التخصيص لاختبارات الوحدة.

أستخدم وحدات الماكرو لتحديد الاستثناءات بسهولة:

DEF_EXCEPTION(RessourceNotFound, "Ressource not found")

حيث يكون DEF_EXCEPTION

#define DEF_EXCEPTION(A, B) class A : public exception\
  {\
  public:\
    virtual const char* what() const throw()\
    {\
      return B;\
    };\
  }\

يمكن للمترجمين رفض طلبك للتضمين.

سيكون لوحدات الماكرو مكانها دائمًا.

الشيء الذي أجده مفيدًا هو #define DEBUG لتتبع التصحيح - يمكنك تركه 1 أثناء تصحيح أخطاء المشكلة (أو حتى تركه قيد التشغيل أثناء دورة التطوير بأكملها) ثم إيقاف تشغيله عندما يحين وقت الشحن.

عندما تتخذ قرارًا في وقت الترجمة بشأن السلوك المحدد للمترجم/نظام التشغيل/الأجهزة.

يسمح لك بجعل واجهتك متوافقة مع ميزات Comppiler/OS/Hardware المحددة.

#if defined(MY_OS1) && defined(MY_HARDWARE1)
#define   MY_ACTION(a,b,c)      doSothing_OS1HW1(a,b,c);}
#elif define(MY_OS1) && defined(MY_HARDWARE2)
#define   MY_ACTION(a,b,c)      doSomthing_OS1HW2(a,b,c);}
#elif define(MY_SUPER_OS)
          /* On this hardware it is a null operation */
#define   MY_ACTION(a,b,c)
#else
#error  "PLEASE DEFINE MY_ACTION() for this Compiler/OS/HArdware configuration"
#endif

في وظيفتي الأخيرة، كنت أعمل على فحص الفيروسات.لتسهيل تصحيح الأخطاء بالنسبة لي، كان لدي الكثير من عمليات التسجيل عالقة في كل مكان، ولكن في تطبيق عالي الطلب مثل هذا، تكون تكلفة استدعاء الوظيفة باهظة الثمن.لذلك، توصلت إلى هذا الماكرو الصغير، الذي لا يزال يسمح لي بتمكين تسجيل التصحيح على إصدار إصدار في موقع العملاء، دون تكلفة استدعاء دالة سيتحقق من علامة التصحيح ويعود فقط دون تسجيل أي شيء، أو إذا تم تمكينه ، سوف أقوم بالتسجيل...تم تعريف الماكرو على النحو التالي:

#define dbgmsg(_FORMAT, ...)  if((debugmsg_flag  & 0x00000001) || (debugmsg_flag & 0x80000000))     { log_dbgmsg(_FORMAT, __VA_ARGS__);  }

نظرًا لوجود VA_ARGS في وظائف السجل، كانت هذه حالة جيدة لماكرو مثل هذا.

قبل ذلك، استخدمت ماكرو في تطبيق عالي الأمان كان يحتاج إلى إخبار المستخدم بأنه ليس لديه حق الوصول الصحيح، وسيخبره بالعلامة التي يحتاجها.

الماكرو (الماكرو) المحدد على النحو التالي:

#define SECURITY_CHECK(lRequiredSecRoles) if(!DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, true)) return
#define SECURITY_CHECK_QUIET(lRequiredSecRoles) (DoSecurityCheck(lRequiredSecRoles, #lRequiredSecRoles, false))

بعد ذلك، يمكننا فقط رش عمليات التحقق في جميع أنحاء واجهة المستخدم، وستخبرك بالأدوار المسموح لها بتنفيذ الإجراء الذي حاولت القيام به، إذا لم يكن لديك هذا الدور بالفعل.وكان السبب في اثنين منهم هو إرجاع قيمة في بعض الأماكن، والعودة من دالة باطلة في أماكن أخرى...

SECURITY_CHECK(ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR);

LRESULT CAddPerson1::OnWizardNext() 
{
   if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_EMPLOYEE) {
      SECURITY_CHECK(ROLE_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD ) -1;
   } else if(m_Role.GetItemData(m_Role.GetCurSel()) == parent->ROLE_CONTINGENT) {
      SECURITY_CHECK(ROLE_CONTINGENT_WORKER_ADMINISTRATOR | ROLE_BUSINESS_INFORMATION_STEWARD | ROLE_WORKER_ADMINISTRATOR) -1;
   }
...

على أي حال، هذه هي الطريقة التي استخدمتها بها، ولست متأكدًا من كيفية المساعدة في ذلك باستخدام القوالب...بخلاف ذلك، أحاول تجنبها، إلا إذا كان ذلك ضروريًا حقًا.

بعد آخر لوحدات الماكرو.ت:النوع، ج:الحاوية، أنا:مكرر

#define foreach(T, c, i) for(T::iterator i=(c).begin(); i!=(c).end(); ++i)
#define foreach_const(T, c, i) for(T::const_iterator i=(c).begin(); i!=(c).end(); ++i)

الاستخدام (عرض المفهوم، وليس حقيقيًا):

void MultiplyEveryElementInList(std::list<int>& ints, int mul)
{
    foreach(std::list<int>, ints, i)
        (*i) *= mul;
}

int GetSumOfList(const std::list<int>& ints)
{
    int ret = 0;
    foreach_const(std::list<int>, ints, i)
        ret += *i;
    return ret;
}

تطبيقات أفضل المتاحة:جوجل "BOOST_FOREACH"

المقالات الجيدة المتاحة: الحب المشروط:فوريتش ريدوكس (إيريك نيبلر) http://www.artima.com/cppsource/foreach.html

ربما يكون الاستخدام الأعظم لوحدات الماكرو هو في التطوير المستقل للمنصة.فكر في حالات عدم تناسق النوع - مع وحدات الماكرو، يمكنك ببساطة استخدام ملفات رأس مختلفة - مثل:--WIN_TYPES.H

typedef ...some struct

--POSIX_TYPES.h

typedef ...some another struct

--program.h

#ifdef WIN32
#define TYPES_H "WINTYPES.H"
#else 
#define TYPES_H "POSIX_TYPES.H"
#endif

#include TYPES_H

يمكن قراءته كثيرًا من تنفيذه بطرق أخرى، في رأيي.

يبدو أنه تم ذكر VA_ARGS بشكل غير مباشر حتى الآن:

عند كتابة كود C++03 عام، وتحتاج إلى عدد متغير من المعلمات (العامة)، يمكنك استخدام ماكرو بدلاً من القالب.

#define CALL_RETURN_WRAPPER(FnType, FName, ...)          \
  if( FnType theFunction = get_op_from_name(FName) ) {   \
    return theFunction(__VA_ARGS__);                     \
  } else {                                               \
    throw invalid_function_name(FName);                  \
  }                                                      \
/**/

ملحوظة: بشكل عام، يمكن أيضًا دمج التحقق من الاسم/رميه في الاسم الافتراضي get_op_from_name وظيفة.هذا مجرد مثال.قد يكون هناك رمز عام آخر يحيط بمكالمة VA_ARGS.

بمجرد أن نحصل على قوالب متنوعة باستخدام C++ 11، يمكننا حل هذه المشكلة "بشكل صحيح" باستخدام قالب.

أعتقد أن هذه الخدعة هي استخدام ذكي للمعالج المسبق الذي لا يمكن محاكاته باستخدام وظيفة:

#define COMMENT COMMENT_SLASH(/)
#define COMMENT_SLASH(s) /##s

#if defined _DEBUG
#define DEBUG_ONLY
#else
#define DEBUG_ONLY COMMENT
#endif

ثم يمكنك استخدامه مثل هذا:

cout <<"Hello, World!" <<endl;
DEBUG_ONLY cout <<"This is outputed only in debug mode" <<endl;

يمكنك أيضًا تحديد ماكرو RELEASE_ONLY.

أنت تستطيع #define الثوابت في سطر أوامر المترجم باستخدام -D أو /D خيار.غالبًا ما يكون هذا مفيدًا عند تجميع نفس البرنامج لمنصات متعددة لأنه يمكنك التحكم في ملفات makefiles الخاصة بك في الثوابت المحددة لكل نظام أساسي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top