문제

기존 코드와 최신 코드에서는 매크로를 사용하여 코드 생성 등과 같은 멋진 솔루션을 수행합니다.그리고 우리는 두 가지 모두를 활용합니다 # 그리고 ## 연산자.

다른 개발자들이 매크로를 사용한다면 어떻게 멋진 일을 하는지 궁금합니다.

도움이 되었습니까?

해결책

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));
}

다른 팁

가장 Coolest Macro IS : Assert, 가드 포함, __file__, __line__.
코드에서 다른 매크로를 사용하지 마십시오.

편집하다:
법적 솔루션이없는 경우에만 매크로를 사용하십시오.

DRY 및 간단한 코드 생성에 유용할 수 있는 X Macro 관용구도 있습니다.

하나는 헤더 gen.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
}

당신은 볼 수 있습니다 부스트 .proprocessor Preprocessor의 흥미로운 용도를 찾으려면 ...

디버깅에 대한 show () :

#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 __/__ line __/etc 매크로에 직접 액세스하여 수행 할 수 있습니다.)

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

이후 편리한 소스-위치 인쇄 메커니즘으로 Message/Warn/Fail Macros에 의해 사용됩니다. 예를 들어:

#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; );

합법적입니다. Assert / Macros가 Codelocation, Stack Traces 또는 STROING 예외 / CoreDumping / EXITING과 같은 모든 종류의 유용한 정보를 자동으로 추가 할 수 없다면 Assert /.


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;

나는 Sean Barrett 에게이 재미있는 사람을 인정합니다.

#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

사전 처리기가 매크로를 통해 표현할 수있는 더 높은 수준의 구조를 기반으로 코드를 생성하도록하는 해킹 방법.

내가 매크로를 사용하는 주요 장소는 내 테스트 프레임 워크입니다. 예를 들어, 일부 코드가 던져야한다고 주장하려면이 매크로를 사용합니다.

#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 );
    ....
};

이것은 특별한 일이 아니라 사람들의 관심을 끌고 쉽게 검색 할 수 있습니다.

임베디드 코드의 경우 embedgurus.com이진 값을 처리할 수 있습니다.

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

이는 약간 확장되었지만 BOOST_BINARY에 대한 @Ferruccio의 이전 응답과 유사한 목표를 달성합니다.

코드는 다음과 같습니다(복사하여 붙여넣었지만 테스트되지 않았습니다. 자세한 내용은 링크를 참조하세요).

// 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 매크로를 사용하여 기본값 (0이 아님)을 가진 구조 리터럴

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. 같은 멤버의 나중에 언급 된이 그림자는 표준에서 잘 정의되어 있습니다.

IE를 위해 반복적 인 것들을 단순화 할 수 있습니다. 열거적인 목록

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 메소드 경계를 통해 예외가 종종 호출 응용 프로그램이 충돌합니다.

방법 브래킷은 이것에 유용합니다. "try"를 포함하는 매크로 인 오프닝 브래킷과 "Catch"ES 세트, 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 매크로는 일부 클리블 프리 프로세서 속임수를 수행하여 C ++에게 바이너리에서 숫자 상수를 발현하는 능력을 제공합니다. 그러나 0-255로 제한됩니다.

그만큼 pthreads 유틸리티 매크로 특히 인상적인 IMHO입니다.

3GPP RRC/NBAP/RNSAP에 사용 된 것과 같은 거대한 C/C ++ 중첩 구조에서 작업 할 때이 트릭을 따라 코드를 깨끗하게 보이게합니다.

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

이렇게하면 유지 보수 시간 동안 수명이 더 쉬워 질 것입니다. 설계 시간과 코드도 읽을 수 있습니다.

유형 안전 및 디버깅 능력을 향상시키기 위해 언어 구성으로 변환합니다.

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

그렇지 않으면 아무 일도 일어나지 않습니다.Gcc는 매우 똑똑한 컴파일러입니다.만약에 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!");
}

매크로에서는 매우 쉽습니다 제어 흐름을합니다 단지 텍스트 대체이기 때문입니다. 다음은 for 루프의 예입니다.

#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