# , ##
전처리기는 스트링 연산과 관련해서 두 개의 연산자를 제공합니다.
하나는 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_FOO
과 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)
__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_FOO
과 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)
__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 이 된다.