# , ##

전처리기는 스트링 연산과 관련해서 두 개의 연산자를 제공합니다. 하나는 double quote 을 해주는 # ( stringizing ) 연산자이고 다른 하나는 두 토큰을 공백 없이 붙여주는 ## ( concatenation ) 연산자입니다.

Stringizing

# ( stringizing ) 연산자는 전달된 인수값을 double quotes 하여 출력합니다.

$ gcpp
#define WARN_IF(EXP) \
do { if (EXP) \
        fprintf (stderr, "Warning: " #EXP "\n"); } \
while (0)

WARN_IF(x == 0);
@
                                  # 전달된 인수값 x == 0 이 double quote 된다.
do { if (x == 0) fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);

매크로 정의에 이 연산자가 사용되면 인수 값은 확장되기 전의 값이 사용됩니다. 다음을 보면 foo 매크로가 hello 로 정의되어 있지만 확장된 값이 사용되지 않고 그대로 foo 가 사용됩니다.

$ gcpp                                $ gcpp
#define QUOTE(s) #s                   #define QUOTE(s) s        // '#' 연산자를 제거
#define foo hello                     #define foo hello

QUOTE(foo)                            QUOTE(foo) 
@                                     @

"foo"                                 hello

확장된 인수값에 # 연산자를 적용하려면 다음과 같이 indirection 을 해야 합니다.

$ gcpp
#define QUOTE_(s) #s 
#define QUOTE(s) QUOTE_(s)      // QUOTE(s) 매크로 내에서는 '#' 연산자가 사용되지 않으므로
#define foo hello               // foo 매크로가 확장된 값이 사용되어 QUOTE_(hello) 가 된다.

QUOTE(foo)
@

"hello"

Concatenation

## 는 token pasting 연산자 입니다. ## 연산자 양쪽에 있는 token 들이 결합되어 하나의 token 으로 됩니다. pasting 된 결과는 전처리기에 적합한 token 이 되어야 합니다.

$ gcpp
#define IO_CONCAT(a, b)  a ## _ ## b
                                            // IO_CONCAT(generic, readl) 부분이
return IO_CONCAT(generic, readl)(addr);     // generic_readl 로 확장된다.
@

return generic_readl(addr);

----------------------------------------

# 다음과 같이 /* */ comment 를 활용하는 방법은 pasting 이 안됩니다.
$ gcpp
#define glue(name,fn) name/**/fn           

glue(foo,bar)
@

foo bar          # foobar 가 안되고 공백이 생긴다.

# ( stringizing ) 연산에서와 같이 ## 연산에서도 확장되기 이전의 인수값이 사용됩니다. 다음을 보면 BEFORE 매크로가 AFTER 로 정의되어 있지만 ## 연산자가 사용된 곳에서는 그대로 BEFORE 가 적용되어 SHOW(test_AFTER, AFTER) 가 아닌 SHOW(test_BEFORE, AFTER) 로 되는 것을 볼 수 있습니다.

$ gcpp
#define BEFORE AFTER
#define SHOW(a, b) show_##a show_##b
#define TEST(a) SHOW(test_##a, a)       // test_BEFORE, AFTER 가 된다.

TEST(BEFORE)                       
@                                     1. TEST(BEFORE)
                                      2. SHOW(test_BEFORE, AFTER)
show_test_BEFORE show_AFTER           3. show_test_BEFORE show_AFTER

마찬가지로 확장된 인수 값이 사용되려면 indirection 을 해야 합니다. 다음을 보면 AFTERX(BUFSIZE) 매크로의 경우 그대로 X_BUFSIZE 가 되지만 한 단계 indirection 을 한 AFTERXX(BUFSIZE) 매크로에서는 BUFSIZE 가 확장되어 X_1024 가 됩니다.

$ gcpp                                 $ gcpp
#define AFTERX(x) X_ ## x              #define AFTERX(x) X_ ## x
#define AFTERXX(x) AFTERX(x)           #define AFTERXX(x) AFTERX(x)
#define TABLESIZE 1024                 #define TABLESIZE 1024
#define BUFSIZE TABLESIZE              #define BUFSIZE TABLESIZE

AFTERX(BUFSIZE)                        AFTERXX(BUFSIZE)
@                                      @

X_BUFSIZE                              X_1024

# , ## 연산자 활용 예

다음은 ### 연산자를 함께 사용하는 예입니다.

__GNUC__predefined macros 입니다.

$ gcpp
#define __gcc_header(x) #x                                      // stringizing
#define _gcc_header(x) __gcc_header(linux/compiler-gcc##x.h)    // concatenation
#define gcc_header(x) _gcc_header(x)

include gcc_header(__GNUC__)
@

include "linux/compiler-gcc9.h"

...............................

1. gcc_header(__GNUC__)                 --->   _gcc_header(9)
2. _gcc_header(9)                       --->   __gcc_header(linux/compiler-gcc9.h)
3. __gcc_header(linux/compiler-gcc9.h)  --->   "linux/compiler-gcc9.h"
struct command {
  char *name;
  void (*function) (void);
};
--------------------------

$ gcpp
#define COMMAND(NAME)  { #NAME, NAME ## _command }

struct command commands[] = {
  COMMAND (quit),   // expands to { "quit", quit_command }
  COMMAND (help),   // expands to { "help", help_command }
};
@

struct command commands[] = {
  { "quit", quit_command },
  { "help", help_command },
};

Quiz

매크로가 정의되어 있을 경우는 1 을 반환하고, 정의되어 있지 않을 경우는 0 을 반환하는 매크로를 만들고 이를 활용하여 AND, OR 연산을 구현하는 것입니다. AND, OR 연산에서도 참일 경우는 1 이 반환되고 거짓일 경우는 0 이 반환됩니다. ( 매크로가 정의될 때는 #define CONFIG_FOO 1 와 같이 1 로 정의됩니다 ).

linux/include/linux/kconfig.h 를 참조하세요.
매크로가 확장되는 단계에 대한 자세한 설명은 Argument Prescan 메뉴를 참조하세요

매크로가 정의되어 있는지 체크하는 __is_defined 매크로

#define CONFIG_FOO 1 일 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define CONFIG_FOO 1
__is_defined(CONFIG_FOO)
@

1
---------------------------------------------------------

1. __is_defined(CONFIG_FOO) 매크로는 ___is_defined(1) 로 확장된다.

2. ___is_defined(1) 는 ## 연산자에 의해 ____is_defined(__ARG_PLACEHOLDER_1) 가 된다.

3. __ARG_PLACEHOLDER_1 매크는 현재 0, 으로 정의되어 있으므로
   arg1_or_junk 값이 0, 으로 치환되어 __take_second_arg(0, 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 1 이 된다.
CONFIG_FOO 매크로가 정의되어 있지 않을 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

__is_defined(CONFIG_FOO)
@                       

0
-------------------------------------------------------------

1. 현재 CONFIG_FOO 매크로는 정의되어 있지 않으므로
   __is_defined(CONFIG_FOO) 매크로는 ___is_defined(CONFIG_FOO) 로 확장된다.

2. ___is_defined(CONFIG_FOO) 는 ## 연산자에 의해 
   ____is_defined(__ARG_PLACEHOLDER_CONFIG_FOO) 로 확장된다.

3. __ARG_PLACEHOLDER_CONFIG_FOO 매크로는 현재 정의되어 있지 않으므로
   arg1_or_junk 값이 __ARG_PLACEHOLDER_CONFIG_FOO 로 치환되어
   __take_second_arg(__ARG_PLACEHOLDER_CONFIG_FOO 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 0 이 된다.

AND

#define CONFIG_FOO 1 그리고 #define CONFIG_BAR 1 일 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __and(x, y)                     ___and(x, y)
#define ___and(x, y)                    ____and(__ARG_PLACEHOLDER_##x, y)
#define ____and(arg1_or_junk, y)        __take_second_arg(arg1_or_junk y, 0)

#define CONFIG_FOO 1
#define CONFIG_BAR 1

__and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@

1
---------------------------------------------------------

1. __and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___and(1, 1) 로 확장된다.

2. ___and(1, 1) 는 ## 연산자에 의해 ____and(__ARG_PLACEHOLDER_1, 1) 로 확장된다.

3. __ARG_PLACEHOLDER_1 매크로 값은 현재 0, 으로 정의되어 있으므로
   arg1_or_junk 값이 0, 으로 치환되어 __take_second_arg(0, 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 1 이 된다.
#define CONFIG_FOO 1 그리고 CONFIG_BAR 는 정의되어 있지 않을 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __and(x, y)                     ___and(x, y)
#define ___and(x, y)                    ____and(__ARG_PLACEHOLDER_##x, y)
#define ____and(arg1_or_junk, y)        __take_second_arg(arg1_or_junk y, 0)

#define CONFIG_FOO 1

__and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@ 

0
----------------------------------------------------------

1. __and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___and(1, 0) 로 확장된다.

2. ___and(1, 0) 는 ## 연산자에 의해 ____and(__ARG_PLACEHOLDER_1, 0) 로 확장된다.

3. __ARG_PLACEHOLDER_1 매크로 값은 현재 0, 으로 정의되어 있으므로
   arg1_or_junk 값이 0, 으로 치환되어 __take_second_arg(0, 0, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 0 이 된다.
CONFIG_FOO 는 정의되어 있지 않고 #define CONFIG_BAR 1 일 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __and(x, y)                     ___and(x, y)
#define ___and(x, y)                    ____and(__ARG_PLACEHOLDER_##x, y)
#define ____and(arg1_or_junk, y)        __take_second_arg(arg1_or_junk y, 0)

#define CONFIG_BAR 1

__and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@

0
----------------------------------------------------------

1. __and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___and(0, 1) 로 확장된다.

2. ___and(0, 1) 는 ## 연산자에 의해 ____and(__ARG_PLACEHOLDER_0, 1) 로 확장된다.

3. __ARG_PLACEHOLDER_0 매크로 값은 현재 정의되어 있지 않으므로 arg1_or_junk 값이
   __ARG_PLACEHOLDER_0 로 치환되어 __take_second_arg(__ARG_PLACEHOLDER_0 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 0 이 된다.
CONFIG_FOOCONFIG_BAR 모두 정의되어 있지 않을 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __and(x, y)                     ___and(x, y)
#define ___and(x, y)                    ____and(__ARG_PLACEHOLDER_##x, y)
#define ____and(arg1_or_junk, y)        __take_second_arg(arg1_or_junk y, 0)

__and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@

0
---------------------------------------------------------

1. __and(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___and(0, 0) 로 확장된다.

2. ___and(0, 0) 는 ## 연산자에 의해 ____and(__ARG_PLACEHOLDER_0, 0) 로 확장된다.

3. __ARG_PLACEHOLDER_0 매크로 값은 현재 정의되어 있지 않으므로 arg1_or_junk 값이
   __ARG_PLACEHOLDER_0 로 치환되어 __take_second_arg(__ARG_PLACEHOLDER_0 0, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 0 이 된다.

OR

#define CONFIG_FOO 1 그리고 #define CONFIG_BAR 1 일 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __or(x, y)                      ___or(x, y)
#define ___or(x, y)                     ____or(__ARG_PLACEHOLDER_##x, y)
#define ____or(arg1_or_junk, y)         __take_second_arg(arg1_or_junk 1, y)

#define CONFIG_FOO 1
#define CONFIG_BAR 1

__or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@

1
--------------------------------------------------------

1. __or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___or(1, 1) 로 확장된다.

2. ___or(1, 1) 는 ## 연산자에 의해 ____or(__ARG_PLACEHOLDER_1, 1) 로 확장된다.

3. __ARG_PLACEHOLDER_1 매크로 값은 현재 0, 으로 정의되어 있으므로
   arg1_or_junk 값이 0, 으로 치환되어 __take_second_arg(0, 1, 1) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 1 이 된다.
#define CONFIG_FOO 1 그리고 CONFIG_BAR 는 정의되어 있지 않을 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __or(x, y)                      ___or(x, y)
#define ___or(x, y)                     ____or(__ARG_PLACEHOLDER_##x, y)
#define ____or(arg1_or_junk, y)         __take_second_arg(arg1_or_junk 1, y)

#define CONFIG_FOO 1

__or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@                                                       

1
---------------------------------------------------------

1. __or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___or(1, 0) 로 확장된다.

2. ___or(1, 0) 는 ## 연산자에 의해 ____or(__ARG_PLACEHOLDER_1, 0) 로 확장된다.

3. __ARG_PLACEHOLDER_1 매크로 값은 현재 0, 으로 정의되어 있으므로
   arg1_or_junk 값이 0, 으로 치환되어 __take_second_arg(0, 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 1 이 된다.
CONFIG_FOO 는 정의되어 있지 않고 #define CONFIG_BAR 1 일 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __or(x, y)                      ___or(x, y)
#define ___or(x, y)                     ____or(__ARG_PLACEHOLDER_##x, y)
#define ____or(arg1_or_junk, y)         __take_second_arg(arg1_or_junk 1, y)

#define CONFIG_BAR 1

__or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@ 

1
--------------------------------------------------------

1. __or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___or(0, 1) 로 확장된다.

2. ___or(0, 1) 는 ## 연산자에 의해 ____or(__ARG_PLACEHOLDER_0, 1) 로 확장된다.

3. __ARG_PLACEHOLDER_0 매크로 값은 현재 정의되어 있지 않으므로 arg1_or_junk 값이
   __ARG_PLACEHOLDER_0 로 치환되어 __take_second_arg(__ARG_PLACEHOLDER_0 1, 1) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 1 이 된다.
CONFIG_FOOCONFIG_BAR 모두 정의되어 있지 않을 경우
$ gcpp
#define __ARG_PLACEHOLDER_1 0,
#define __take_second_arg(__ignored, val, ...) val

#define __is_defined(x)                 ___is_defined(x)
#define ___is_defined(val)              ____is_defined(__ARG_PLACEHOLDER_##val)
#define ____is_defined(arg1_or_junk)    __take_second_arg(arg1_or_junk 1, 0)

#define __or(x, y)                      ___or(x, y)
#define ___or(x, y)                     ____or(__ARG_PLACEHOLDER_##x, y)
#define ____or(arg1_or_junk, y)         __take_second_arg(arg1_or_junk 1, y)

__or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR))
@                                                       

0
---------------------------------------------------------

1. __or(__is_defined(CONFIG_FOO), __is_defined(CONFIG_BAR)) 는 ___or(0, 0) 로 확장된다.

2. ___or(0, 0) 는 ## 연산자에 의해 ____or(__ARG_PLACEHOLDER_0, 0) 으로 확장된다.

3. __ARG_PLACEHOLDER_0 매크로 값은 현재 정의되어 있지 않으므로 arg1_or_junk 값이
   __ARG_PLACEHOLDER_0 로 치환되어 __take_second_arg(__ARG_PLACEHOLDER_0 1, 0) 으로 확장된다.

4. __take_second_arg 매크로 정의에 따라 두 번째 인수 값을 취하게 되면 결과는 0 이 된다.