C에서 이와 같은 작업을해야합니다. 문자를 사용하는 경우에만 작동하지만 문자열이 필요합니다. 어떻게 할 수 있습니까?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
C에서 이와 같은 작업을해야합니다. 문자를 사용하는 경우에만 작동하지만 문자열이 필요합니다. 어떻게 할 수 있습니까?
#define USER "jack" // jack or queen
#if USER == "jack"
#define USER_VS "queen"
#elif USER == "queen"
#define USER_VS "jack"
#endif
답변:
전 처리기 지시문에서 가변 길이 문자열 비교를 완전히 수행하는 방법이 없다고 생각합니다. 그래도 다음을 수행 할 수 있습니다.
#define USER_JACK 1
#define USER_QUEEN 2
#define USER USER_JACK
#if USER == USER_JACK
#define USER_VS USER_QUEEN
#elif USER == USER_QUEEN
#define USER_VS USER_JACK
#endif
또는 코드를 약간 리팩토링하고 대신 C 코드를 사용할 수 있습니다.
#define USER_VS (3 - USER)
이 특정한 경우에 할 수 있습니다. :)
[업데이트 : 2018.05.03]
주의 사항 : 모든 컴파일러가 동일한 방식으로 C ++ 11 사양을 구현하는 것은 아닙니다. 아래 코드는 내가 테스트 한 컴파일러에서 작동하지만 많은 주석가는 다른 컴파일러를 사용했습니다.
Shafik Yaghmour의 답변에서 인용 : 컴파일 시간에 C 문자열의 길이 계산. 이것은 정말로 constexpr입니까?
상수 표현식은 컴파일 타임에 평가된다는 보장이 없습니다. 우리는 C ++ 표준 섹션 5.19 상수 표현식 초안에서 비표준적인 인용문 만 가지고 있습니다.
[...]> [참고 : 변환 중에 상수 표현식을 평가할 수 있습니다 .—end note]
그 단어 can
는 세상의 모든 차이를 만듭니다.
따라서 constexpr
컴파일러 작성자의 사양 해석에 따라이 (또는 모든) 답변에 대한 YMMV .
[2016.01.31 업데이트]
일부는 문자열 비교가 필요없는 목표를 달성하여 OP 의 전체 측면을 피 했기 때문에 이전 답변을 좋아하지 않았기 때문에 compile time string compare
여기에 더 자세한 답변이 있습니다.
당신은 할 수 없습니다! C98 또는 C99에는 없습니다. C11에서도 마찬가지입니다. MACRO 조작의 양은이를 변경하지 않습니다.
에서 const-expression
사용되는 정의는 #if
문자열을 허용하지 않습니다.
문자를 허용하므로 문자로 제한하면 다음을 사용할 수 있습니다.
#define JACK 'J'
#define QUEEN 'Q'
#define CHOICE JACK // or QUEEN, your choice
#if 'J' == CHOICE
#define USER "jack"
#define USER_VS "queen"
#elif 'Q' == CHOICE
#define USER "queen"
#define USER_VS "jack"
#else
#define USER "anonymous1"
#define USER_VS "anonymous2"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
할 수 있습니다! C ++ 11에서. 비교를 위해 컴파일 시간 도우미 함수를 정의한 경우.
// compares two strings in compile time constant fashion
constexpr int c_strcmp( char const* lhs, char const* rhs )
{
return (('\0' == lhs[0]) && ('\0' == rhs[0])) ? 0
: (lhs[0] != rhs[0]) ? (lhs[0] - rhs[0])
: c_strcmp( lhs+1, rhs+1 );
}
// some compilers may require ((int)lhs[0] - (int)rhs[0])
#define JACK "jack"
#define QUEEN "queen"
#define USER JACK // or QUEEN, your choice
#if 0 == c_strcmp( USER, JACK )
#define USER_VS QUEEN
#elif 0 == c_strcmp( USER, QUEEN )
#define USER_VS JACK
#else
#define USER_VS "unknown"
#endif
#pragma message "USER IS " USER
#pragma message "USER_VS IS " USER_VS
따라서 궁극적으로 USER
및에 대한 최종 문자열 값을 선택하려는 목표를 달성하는 방식을 변경해야합니다 USER_VS
.
C99에서는 컴파일 시간 문자열 비교를 할 수 없지만 컴파일 시간에 문자열을 선택할 수 있습니다.
컴파일 시간 스팅 비교를 수행해야하는 경우 해당 기능을 허용하는 C ++ 11 또는 최신 변형으로 변경해야합니다.
[원래 답변]
시험:
#define jack_VS queen
#define queen_VS jack
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
// stringify usage: S(USER) or S(USER_VS) when you need the string form.
#define S(U) S_(U)
#define S_(U) #U
업데이트 : ANSI 토큰 붙여 넣기는 때때로 명확하지 않습니다. ;-디
#
매크로 앞에 단일을 넣으면 베어 값 대신 해당 값의 문자열로 변경됩니다.
##
두 토큰 사이 에 double을 넣으면 토큰이 단일 토큰으로 연결됩니다.
따라서 매크로 USER_VS
는 설정 방법에 따라 jack_VS
또는 .queen_VS
USER
캐릭터 라인 화 매크로 S(...)
명명 된 매크로의 값이 문자열로 변환됩니다 있도록 매크로 간접를 사용합니다. 매크로 이름 대신.
따라서 USER##_VS
이된다 jack_VS
(또는 queen_VS
사용자가 설정 한 방식에 따라) USER
.
나중에 stringify 매크로가 ( 이 예에서) S(USER_VS)
의 값으로 사용되면 해당 값 ( )을 문자열로 변환 하는 간접 단계로 전달됩니다 .USER_VS
jack_VS
S_(jack_VS)
queen
"queen"
사용자가 설정 한 경우 USER
에 queen
그 최종 결과는 문자열입니다 "jack"
.
토큰 연결에 대해서는 https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html을 참조하십시오.
토큰 문자열 변환에 대해서는 https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification을 참조하십시오.
[오타 수정을 위해 2015.02.15 업데이트 됨]
#if 0 == c_strcmp( USER, JACK )
에constexpr int comp1 = c_strcmp( USER, JACK );
#if 0 == comp1
#if
. USER가 JACK이기 때문에 예제가 작동합니다. 사용자가 QUEEN 있었다면, 그것은 말할 것 USER IS QUEEN
와USER_VS IS QUEEN
constexpr
전 처리기 지시문에서 함수를 호출 할 수 없습니다 .
다음은 clang으로 나를 위해 일했습니다. 기호 매크로 값 비교로 나타나는 것을 허용합니다. #error xxx 는 컴파일러가 실제로하는 일을 보는 것입니다. 교체 고양이 와 정의를 #DEFINE 고양이 (A, B) B A ## 휴식 것들.
#define cat(a,...) cat_impl(a, __VA_ARGS__)
#define cat_impl(a,...) a ## __VA_ARGS__
#define xUSER_jack 0
#define xUSER_queen 1
#define USER_VAL cat(xUSER_,USER)
#define USER jack // jack or queen
#if USER_VAL==xUSER_jack
#error USER=jack
#define USER_VS "queen"
#elif USER_VAL==xUSER_queen
#error USER=queen
#define USER_VS "jack"
#endif
위에서 이미 언급했듯이 ISO-C11 전처리 기는 문자열 비교를 지원 하지 않습니다 . 그러나 "반대 값"으로 매크로를 할당하는 문제는 "토큰 붙여 넣기"및 "테이블 액세스"로 해결할 수 있습니다. Jesse의 간단한 concatenate / stringify 매크로 솔루션 은 연결 평가 전에 문자열 화가 수행 되기 때문에 gcc 5.4.0에서 실패합니다 (ISO C11 준수). 그러나 다음과 같이 수정할 수 있습니다.
#define P_(user) user ## _VS
#define VS(user) P_ (user)
#define S(U) S_(U)
#define S_(U) #U
#define jack_VS queen
#define queen_VS jack
S (VS (jack))
S (jack)
S (VS (queen))
S (queen)
#define USER jack // jack or queen, your choice
#define USER_VS USER##_VS // jack_VS or queen_VS
S (USER)
S (USER_VS)
(매크로 첫 번째 줄은 P_()
) 다음 라인 (매크로 수 있도록 한 간접 추가 VS()
) 병합을 완료 하기 전에 stringization을합니다 ( 왜 매크로에 대한 간접 더블 레이어가 필요하십니까? ). 스트링 화 매크로 ( S()
및 S_()
)는 Jesse의 것입니다.
OP의 if-then-else 구성보다 유지 관리가 훨씬 쉬운 테이블 (매크로 jack_VS
및 queen_VS
)은 Jesse에서 가져온 것입니다.
마지막으로 다음 4 줄 블록은 함수 스타일 매크로를 호출합니다. 마지막 4 줄 블록은 Jesse의 답변에서 가져온 것입니다.
코드를 저장하고 foo.c
전처리기를 호출 gcc -nostdinc -E foo.c
하면 다음이 생성됩니다.
# 1 "foo.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "foo.c"
# 9 "foo.c"
"queen"
"jack"
"jack"
"queen"
"jack"
"USER_VS"
예상대로 출력됩니다. 마지막 줄은 문자열 화 전에 USER_VS
매크로가 확장 되지 않았 음을 보여줍니다 .
#if (S(USER)=="jack")
.-나는 "
-를 사용할 때 전 처리기 오류가 발생합니다 error: invalid token at start of a preprocessor expression
.
문자열이 컴파일 시간 상수 인 경우 (귀하의 경우) 다음 트릭을 사용할 수 있습니다.
#define USER_JACK strcmp(USER, "jack")
#define USER_QUEEN strcmp(USER, "queen")
#if $USER_JACK == 0
#define USER_VS USER_QUEEN
#elif USER_QUEEN == 0
#define USER_VS USER_JACK
#endif
컴파일러는 strcmp의 결과를 미리 알려줄 수 있으며 strcmp를 결과로 대체하여 전 처리기 지시문과 비교할 수있는 #define을 제공합니다. 컴파일러 옵션에 대한 컴파일러 / 종속성간에 차이가 있는지 모르겠지만 GCC 4.7.2에서 저에게 효과적이었습니다.
편집 : 추가 조사 결과, 이것이 GCC 확장이 아닌 도구 체인 확장 인 것처럼 보이므로이를 고려하십시오.
$
사전 프로세서 확장의 어떤 종류는?
Patrick 과 Jesse Chisholm 의 답변 은 다음과 같이했습니다.
#define QUEEN 'Q'
#define JACK 'J'
#define CHECK_QUEEN(s) (s==QUEEN)
#define CHECK_JACK(s) (s==JACK)
#define USER 'Q'
[... later on in code ...]
#if CHECK_QUEEN(USER)
compile_queen_func();
#elif CHECK_JACK(USER)
compile_jack_func();
#elif
#error "unknown user"
#endif
대신에 #define USER 'Q'
#define USER QUEEN
작동해야하지만 테스트되지 않았습니다. 또한 작동하며 다루기가 더 쉬울 수 있습니다.
편집 : @ Jean-François Fabre의 의견에 따르면 내 대답을 수정했습니다.
(s==QUEEN?1:0)
에 의해 (s==QUEEN)
당신은 결과가 이미 부울이다, 삼항 필요하지 않습니다
#define USER_IS(c0,c1,c2,c3,c4,c5,c6,c7,c8,c9)\
ch0==c0 && ch1==c1 && ch2==c2 && ch3==c3 && ch4==c4 && ch5==c5 && ch6==c6 && ch7==c7 ;
#define ch0 'j'
#define ch1 'a'
#define ch2 'c'
#define ch3 'k'
#if USER_IS('j','a','c','k',0,0,0,0)
#define USER_VS "queen"
#elif USER_IS('q','u','e','e','n',0,0,0)
#define USER_VS "jack"
#endif
그것은 기본적으로 가변 길이 대신 수동으로 초기화되는 고정 길이 정적 문자 배열입니다.
USER가 따옴표로 묶인 문자열로 정의 된 경우에는 그렇게 할 수 없습니다.
하지만 USER가 JACK, QUEEN, Joker 등이라면 그렇게 할 수 있습니다 .
사용할 수있는 두 가지 트릭이 있습니다.
#define JACK
무언가 를 할 필요없이 JACK과 비교할 수있게합니다이제 다음과 같이 시작하겠습니다.
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
이제 내가 쓰고 JACK_QUEEN_OTHER(USER)
USER가 JACK이면 전처리 기가 그것을 다음과 같이 바꿉니다.EXPANSION1(ReSeRvEd_, JACK, 1, 2, 3)
2 단계는 연결입니다.
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
이제 JACK_QUEEN_OTHER(USER)
이된다EXPANSION2(ReSeRvEd_JACK, 1, 2, 3)
이렇게하면 문자열이 일치하는지 여부에 따라 여러 개의 쉼표를 추가 할 수 있습니다.
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
사용자가 JACK하면 JACK_QUEEN_OTHER(USER)
된다EXPANSION2(x,x,x, 1, 2, 3)
사용자가 QUEEN 인 경우, JACK_QUEEN_OTHER(USER)
이된다EXPANSION2(x,x, 1, 2, 3)
사용자가 다른 경우 JACK_QUEEN_OTHER(USER)
가된다EXPANSION2(ReSeRvEd_other, 1, 2, 3)
이 시점에서 중요한 일이 발생했습니다. EXPANSION2 매크로에 대한 네 번째 인수는 전달 된 원래 인수가 jack, queen 또는 다른 어떤 것인지에 따라 1, 2 또는 3입니다. 그래서 우리가해야 할 일은 그것을 골라내는 것입니다. 장기적인 이유로 마지막 단계에서 두 개의 매크로가 필요합니다. 불필요한 것처럼 보이지만 EXPANSION2 및 EXPANSION3입니다.
종합하면 다음과 같은 6 개의 매크로가 있습니다.
#define JACK_QUEEN_OTHER(u) EXPANSION1(ReSeRvEd_, u, 1, 2, 3)
#define EXPANSION1(a, b, c, d, e) EXPANSION2(a##b, c, d, e)
#define EXPANSION2(a, b, c, d, ...) EXPANSION3(a, b, c, d)
#define EXPANSION3(a, b, c, d, ...) d
#define ReSeRvEd_JACK x,x,x
#define ReSeRvEd_QUEEN x,x
그리고 다음과 같이 사용할 수 있습니다.
int main() {
#if JACK_QUEEN_OTHER(USER) == 1
printf("Hello, Jack!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 2
printf("Hello, Queen!\n");
#endif
#if JACK_QUEEN_OTHER(USER) == 3
printf("Hello, who are you?\n");
#endif
}
필수 godbolt 링크 : https://godbolt.org/z/8WGa19