ما هي بعض الحيل التي يمكن استخدامها مع وحدات الماكرو ؟ [مغلقة]

StackOverflow https://stackoverflow.com/questions/650461

سؤال

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

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

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

المحلول

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

// could evaluate at compile time if __builtin_sin gets
// special treatment by the compiler
#define sin(x) __builtin_sin(x)

// parentheses avoid substitution by the macro
double (sin)(double arg) {
    return sin(arg); // uses the macro
}

int main() {
    // uses the macro
    printf("%f\n", sin(3.14));

    // uses the function
    double (*x)(double) = &sin;

    // uses the function
    printf("%f\n", (sin)(3.14));
}

نصائح أخرى

أروع الكلي هو:تأكيد ، تشمل الحراس ، __FILE__, __الخط__.
تجنب استخدام الماكرو الأخرى في التعليمات البرمجية الخاصة بك.

تحرير:
استخدام وحدات الماكرو فقط عندما لا يكون لديك حل قانوني w/o لهم.

هناك أيضا X الكلي لغة التي يمكن أن تكون مفيدة للشعر الجاف رمز بسيط الجيل :

أحد يعرف في رأس الجنرال.× نوع من الجدول باستخدام لم تعرف حتى الآن الماكرو :

/** 1st arg is type , 2nd is field name , 3rd is initial value , 4th is help */
GENX( int , "y" , 1 , "number of ..." );
GENX( float , "z" , 6.3 , "this value sets ..." );
GENX( std::string , "name" , "myname" , "name of ..." );

ثم انه يمكن استخدامه في أماكن مختلفة في تحديد ذلك لكل #include مع عادة مختلفة التعريف :

class X
{
public :

     void setDefaults()
     {
#define GENX( type , member , value , help )\
         member = value ;
#include "gen.x"
#undef GENX
     }

     void help( std::ostream & o )
     {
#define GENX( type , member , value , help )\
          o << #member << " : " << help << '\n' ;
#include "gen.x"
#undef GENX
     }

private :

#define GENX( type , member , value , help )\
     type member ;
#include "gen.x"
#undef GENX
}

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

وعرض () للتصحيح:

#define SHOW(X) cout << # X " = " << (X) << endl

ووالمزدوج التقييم لتوسيع الحجج خدعة: (على سبيل المثال، استخدم رقم السطر الفعلي وليس "__LINE __")

    /* Use CONCATENATE_AGAIN to expand the arguments to CONCATENATE */
#define CONCATENATE(      x,y)  CONCATENATE_AGAIN(x,y)
#define CONCATENATE_AGAIN(x,y)  x ## y

وثابت تأكيدات وقت الترجمة.
منها مثلا:

#define CONCATENATE_4(      a,b,c,d)  CONCATENATE_4_AGAIN(a,b,c,d)
#define CONCATENATE_4_AGAIN(a,b,c,d)  a ## b ## c ## d

    /* Creates a typedef that's legal/illegal depending on EXPRESSION.       *
     * Note that IDENTIFIER_TEXT is limited to "[a-zA-Z0-9_]*".              *
     * (This may be replaced by static_assert() in future revisions of C++.) */
#define STATIC_ASSERT( EXPRESSION, IDENTIFIER_TEXT)                     \
  typedef char CONCATENATE_4( static_assert____,      IDENTIFIER_TEXT,  \
                              ____failed_at_line____, __LINE__ )        \
            [ (EXPRESSION) ? 1 : -1 ]

والمستخدمة عن طريق:

typedef  int32_t  int4;

STATIC_ASSERT( sizeof(int4) == 4, sizeof_int4_equal_4 );

وتهيئة مثيل CodeLocation الدرجة: (تخزين ملف / الخط / وظيفة من وجهة الاحتجاج - وهذا يمكن * فقط * أن يتم ذلك مع ماكرو أو عن طريق الدخول مباشرة __FILE __ / __ خط __ / الخ وحدات الماكرو عند نقطة المصدر. )

        /* Note:  Windows may have __FUNCTION__.  C99 defines __func__. */
#define CURRENT_CODE_LOCATION()  \
           CodeLocation( __PRETTY_FUNCTION__, __FILE__, __LINE__ )

واستخدامها في وقت لاحق من قبل MESSAGE / يحذرون / فشل وحدات الماكرو كآلية الطباعة مصدر في الموقع مريحة. على سبيل المثال:

#define WARN_IF_NAN(X)                                      \
  do                                                        \
  {                                                         \
    if ( isnan(X) != 0 )                                    \
      WARN( # X " is NaN (Floating Point NOT-A-NUMBER)" );  \
    if ( isinf(X) != 0 )                                    \
      WARN( # X " is INF (Floating Point INFINITY)" );      \
  } while ( false )

وتأكيد / ما لم حدات الماكرو. يمكنك تمرير أي رمز، بما في ذلك شركات مثل '=='، من خلال الماكرو. لذلك يبني مثل:

ASSERT( foo, ==, bar )

أو

UNLESS( foo, >=, 0, value=0; return false; );

هل القانوني. تأكيد / ما لم حدات الماكرو يمكن أن تضاف تلقائيا كل أنواع المعلومات المفيدة لطيفة مثل CodeLocation، كومة آثار، أو رمي الاستثناءات / coredumping / الخروج بأمان.


وجعل errno أبسط:

#define ERRNO_FORMAT  "errno= %d (\"%s\")"
#define ERRNO_ARGS    errno, strerror(errno)
#define ERRNO_STREAM  "errno= " << errno << " (\"" << strerror(errno) << "\") "

ومنها مثلا. printf ( "فشل فتح". ERRNO_FORMAT، ERRNO_ARGS)؛

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

#define TRACE( allargs) do { printf allargs; } while ( 0)
...
TRACE(( "%s %s\n", "Help", "me"));

وتسجيل هو مكان واحد حيث يتم في كثير من الأحيان لا سيما استخدام وحدات الماكرو:

#define LOG(log) \
  if (!log.enabled()) {} \
  else log.getStream() << __FILE__ << "@" << __LINE__ << ": "


log_t errorlog;
...

LOG(errorlog) << "This doesn't look good:" << somedata;

وأنا الائتمان شون باريت عن هذه المتعة واحد:

#ifndef blah
    #define blah(x) // something fun
    #include __FILE__
    #undef blah
#endif

#ifndef blah
    #define blah(x) // something else that is also fun
    #include __FILE__
    #undef blah
#endif

#ifdef blah
    blah(foo)
    blah(bar)
#endif

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

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

#define MUST_THROW( expr )                       
  try {                                
    (expr);                              
    (myth_suite_).Fail( #expr +                    
            std::string( " should throw but didn't" ) );  
  }                                  
  catch( ... ) {                            
  }                                  

واستخدامها مثل هذا:

MUST_THROW( some_bogus_stuff() );
MUST_THROW( more_bogus_stuff() );

والمكان الآخر الوحيد الذي يمكنني استخدامها في الإعلانات الصف. لدي ماكرو:

#define CANNOT_COPY( cls )              \
  private:                              \
    cls( const cls & );                 \
    void operator=( const cls & )       \

والتي يمكنني استخدامها لتحديد أن فئة لا يمكن نسخ (أو تعيين):

class BankAccount {

    CANNOT_COPY( BankAccount );
    ....
};

وهذا لا تفعل أي شيء خاص ولكن يلفت الانتباه الشعوب ويمكن بسهولة البحث عنها.

لالتعليمات البرمجية المضمنة، خدعة لطيفة من embeddedgurus.com تمكنك من التعامل مع قيم ثنائية:

B8(01010101) // 85
B16(10101010,01010101) // 43,605
B32(10000000,11111111,10101010,01010101) // 2,164,238,93

وهذا تحقيق أهداف مماثلة لاستجابة السابقة منFerruccio حول BOOST_BINARY، على الرغم من أن قليلا توسيع نطاقها.

وهنا الرمز (copy'n لصق، لم تختبر، انظر الى الوصلة للمزيد من التفاصيل)

// Internal Macros
#define HEX__(n) 0x##n##LU
#define B8__(x) ((x&0x0000000FLU)?1:0) \
  +((x&0x000000F0LU)?2:0) \
  +((x&0x00000F00LU)?4:0) \
  +((x&0x0000F000LU)?8:0) \
  +((x&0x000F0000LU)?16:0) \
  +((x&0x00F00000LU)?32:0) \
  +((x&0x0F000000LU)?64:0) \
  +((x&0xF0000000LU)?128:0)

// User-visible Macros
#define B8(d) ((unsigned char)B8__(HEX__(d)))
#define B16(dmsb,dlsb) (((unsigned short)B8(dmsb)<<8) + B8(dlsb))
#define B32(dmsb,db2,db3,dlsb) \
  (((unsigned long)B8(dmsb)<<24) \
  + ((unsigned long)B8(db2)<<16) \
  + ((unsigned long)B8(db3)<<8) \
  + B8(dlsb))

وأنا أحب وحدات الماكرو. الكثير من المرح عند التصحيح!

وأنا غالبا ما لف أشياء مثل السونار التصحيح في ماكرو بسيط الذي يسمح ليتم تجميعها من الإفراج يبني:

#ifdef DEBUG
#define D(s) do { s; } while(0)
#else
#define D(s) do {/**/} while(0)
#endif

والاستخدام عادة في وقت لاحق شيء من هذا القبيل:

D(printf("level %d, condition %s\n", level, condition));

وولغة do{}while(0) هناك لتجنب المشاكل التي قد تنجم عن جعل بطريق الخطأ استخدام D(...) المحتوى فقط من مشروطا أو حلقة. كنت لا تريد كود مثل هذا يعني الشيء الخطأ، بعد كل شيء:

for(i=1;i<10;++i) D(printf("x[%d]=%f\n",i,x[i]));
SomeReallyExpensiveFunction(x);

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

وأنا أيضا مروحة كبيرة من التأكيدات وقت الترجمة. بلدي صياغة مختلفة قليلا، ولكن لا يوجد لديه المزايا الحقيقية مقارنة مع الآخرين رأيت. إن الحل يكمن في تشكيل typedef واسمه فريد أن يلقي خطأ إذا كان الشرط أكد خاطئة، وليس غير ذلك. في cassert.h لدينا:

/*! \brief Compile-time assertion.
 *
 *  Note that the cassert() macro generates no code, and hence need not
 *  be restricted to debug builds.  It does have the side-effect of
 *  declaring a type name with typedef.  For this reason, a unique
 *  number or string of legal identifier characters must be included
 *  with each invocation to avoid the attempt to redeclare a type.
 *
 *  A failed assertion will attempt to define a type that is an array
 *  of -1 integers, which will throw an error in any standards
 *  compliant compiler. The exact error is implementation defined, but
 *  since the defined type name includes the string "ASSERTION" it
 *  should trigger curiosity enough to lead the user to the assertion
 *  itself.
 *
 *  Because a typedef is used, cassert() may be used inside a function,
 *  class or struct definition as well as at file scope.
 */
#define cassert(x,i) typedef int ASSERTION_##i[(x)?1:-1]

وفي بعض الملفات المصدر، في أي مكان في typedef وسيكون القانوني:

#include "cassert.h"
...
cassert(sizeof(struct foo)==14, foo1);
...

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

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

والحرفية البنية مع القيم الافتراضية (التي ليست صفر)، وذلك باستخدام وحدات الماكرو C99 variadic

struct Example {
   int from;
   int to;
   const char *name;
}

#define EXAMPLE(...) ((struct Example){.from=0, .to=INT_MAX, .name="", __VA_ARGS__})

وباستخدام يستخدم EXAMPLE(.name="test") القيم الافتراضية، عدا تجاوز صريح للname. هذا التظليل مع وقت لاحق يذكر من يعرف جيدا من العضو نفسه في المعيار.

ويمكن للمرء أن تبسيط الأمور المتكررة لأي. قوائم التعداد

enum {
  kOneEnum,
  kTwoEnum,
  kThreeEnum,
  kFourEnum
};

... وبعد قيام حالة التبديل على طريقة منظمة

#define TEST( _v ) \
    case k ## _v ## Enum: \
      CallFunction ## _v(); \
      break;

switch (c) {
    TEST( One   );
    TEST( Two   );
    TEST( Three );
    TEST( Four  );
}

ملحوظة <م>: بالتأكيد يمكن أن يتم ذلك مع مجموعة وظيفة مؤشر ولكن هذا يفتح عن القليل من المزيد من المرونة لإضافة معلمات وأيضا استخدام التوسعات سلسلة مع تجزئة واحدة

... أو لاختبار على الاوتار للحصول على قيمة التعداد الصحيح

int value = -1;
char *str = getstr();

#define TEST( _v ) \
    if (!strcmp(# _v, str)) \
        value = k ## _v ## Enum

TEST( One   );
TEST( Two   );
TEST( Three );
TEST( Four  );

ويمكنك استخدام وحدات الماكرو لتحديد نفس الوظائف مع أنواع بيانات مختلفة. على سبيل المثال:

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>

#define DEFINE_BITS_STR(name, type)               \
char *bits_str_##name(type value)                 \
{                                                 \
    int len = sizeof(type) * CHAR_BIT;            \
    char *result;                                 \
    type n;                                       \
    int i;                                        \
                                                  \
    result = (char *)calloc(len+1, sizeof(type)); \
    if(result == NULL)                            \
        return NULL;                              \
                                                  \
    memset(result, '0', len);                     \
    result[len] = 0x00;                           \
                                                  \
    n = value;                                    \
    i = len;                                      \
    while(n)                                      \
    {                                             \
        if(n & 1)                                 \
            result[i] = '1';                      \
                                                  \
        n >>= 1;                                  \
        --i;                                      \
    }                                             \
                                                  \
    return result;                                \
}

DEFINE_BITS_STR(uchar, unsigned char)
DEFINE_BITS_STR(uint, unsigned int)
DEFINE_BITS_STR(int, unsigned int)

int main()
{
    unsigned char value1 = 134;
    unsigned int value2 = 232899;
    int value3 = 255;
    char *ret;

    ret = bits_str_uchar(value1);
    printf("%d: %s\n", value1, ret);

    ret = bits_str_uint(value2);
    printf("%d: %s\n", value2, ret);

    ret = bits_str_int(value3);
    printf("%d: %s\n", value3, ret);

    return 1;
}

في هذا المثال يحدد ثلاث وظائف (bits_str_uchar()، bits_str_uint()، bits_str_int()) التي تتعامل مع ثلاثة أنواع مختلفة من البيانات (unsigned char، unsigned int، int). ومع ذلك، كل إرجاع سلسلة تحتوي على أجزاء من القيمة التي تم تمريرها.

عند تنفيذ الخادم COM، عليك أن تأخذ الرعاية من جميع الاستثناءات التعليمات البرمجية ربما يمكن رمي - السماح استثناء من خلال أسلوب COM الحدود وغالبا ما تعطل تطبيق الاستدعاء

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

ومن المشروع CrashRpt، تحتاج خدعة لتوسيع وحدات الماكرو ويعرف:

#define WIDEN2(x) L ## x 
#define WIDEN(x) WIDEN2(x)
std::wstring BuildDate = std::wstring(WIDEN(__DATE__)) + L" " + WIDEN(__TIME__);

ومعظم مبنية (كل شيء؟) أطر C ++ وحدة اختبار على وحدات الماكرو. نحن نستخدم UnitTest ++ . التحقق من ذلك لمعرفة جميع أنواع وحدات الماكرو الهوى.

BOOST_BINARY ماكرو يقوم بعض clevel الخداع قبل المعالج لإعطاء C ++ القدرة على التعبير عن الثوابت الرقمية في ثنائي. فهو يقتصر على 0-255 ولكن.

بثريدس وحدات الماكرو فائدة غير IMHO إعجاب particularily.

عند أعمل على ج / ج ++ البنيات المتداخلة ضخمة مثل تلك المستخدمة ل3GPP RRC / NBAP / RNSAP، أنا اتبع هذه خدعة لجعل رمز تبدو نظيفة.

struct leve1_1
{
  int data;

  struct level2
  {
    int data;

    struct level3
    {
      int data;
    } level_3_data;

  } level_2_data;

} level_1_data;

level_1_data.data = 100;

#define LEVEL_2 leve1_1_data.level_2_data
LEVEL_2.data = 200;

#define LEVEL_3 LEVEL_2.level_3_data
LEVEL_3.data = 300;

#undef LEVEL_2
#undef LEVEL_3

وهذا سيجعل الحياة أسهل خلال time..also الصيانة في وقت التصميم ورمز سوف تكون قابلة للقراءة.

وتحويلها إلى بناء اللغة لتحسين السلامة على نوع وقدرة التصحيح.

void _zero_or_die(int v, const char* filename, int line)
{
    if (v != 0)
    {
       fprintf(stderr, "error %s:%d\n", filename, line);
       exit(1);
    }
}

#define ZERO_OR_DIE_ for (int _i=1; _i == 1; _zero_or_die(_i, __FILE__, __LINE__)) _i=



ZERO_OR_DIE_   pipe(fd);
ZERO_OR_DIE_   close(0);
ZERO_OR_DIE_   sigaction(SIGSEGV, &sigact, NULL);
ZERO_OR_DIE_   pthread_mutex_lock(&mt);
ZERO_OR_DIE_   pthread_create(&pt, NULL, func, NULL);

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

وهذا هو ماكرو بسيط هو أن أثبت مفيدة جدا:

#define DEBUG_OUT(value) sprintf(uartTxBuf, "%s = 0x%04X\n", #value, value);\
                         puts_UART((uint16_t *) uartTxBuf)

والاستخدام سبيل المثال:

for (i=0; i < 4; i++)
{
    DEBUG_OUT(i);
    DEBUG_OUT(i % 3);
}

وتيار تلقي:

i = 0x0000
i % 3 = 0x0000
i = 0x0001
i % 3 = 0x0001
i = 0x0002
i % 3 = 0x0002
i = 0x0003
i % 3 = 0x0000

نعم، انها الخام وغير آمنة. انها تطبق فقط حتى يتم عزل الشوائب، لذلك هذا الماكرو لا ضرر ولا ضرار.

وكثيرا ما يمكنني استخدام هذا. لدي رأس debug.h تحديد ما يلي:

#ifndef DEBUG_H
#define DEBUG_H
    #ifdef DEBUG
    #define debuf if(1)
    #else
    #define debug if(0)
    #endif
#endif

وبعد ذلك:

debug {
   printf("message from debug!");
}

وإذا كنت ترغب في الحصول على رسالة "message from debug!"، ترجمة مع:

gcc -D DEBUG foo.c

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

وكنت لا تزال تستطيع أن تفعل أكثر من ذلك:

debug 
{
   pritnf("I'm in debug mode!\n");
} 
else 
{
  printf("I'm not in debug mode\n");
}

وبعض أيام منذ رأيت اللغة D البرمجة توفير ميزة مشابهة جدا جدا.

إذا كنت تعتقد أن أعلاه دون السياق، يمكنك تحديد يعتقد أنه

#define in_debug if(1)
#define not_debug else

وبعد ذلك

in_debug {
  printf("I'm in debug mode!");
}
not_debug {
  printf("Not in debug mode!");
}

في وحدات الماكرو فإنه من السهل جدا أن هل التحكم في التدفق لأنه مجرد النص البديل.هنا مثال عن الحلقة:

#include <stdio.h>

#define loop(i,x) for(i=0; i<x; i++)

int main(int argc, char *argv[])
{
    int i;
    int x = 5;
    loop(i, x)
    {
        printf("%d", i); // Output: 01234
    } 
    return 0;
} 
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top