[c] C 조건부 전 처리기 지시문에서 문자열을 비교하는 방법

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 코드를 사용할 수 있습니다.


답변

[업데이트 : 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_VSUSER

캐릭터 라인 화 매크로 S(...)명명 된 매크로의 값이 문자열로 변환됩니다 있도록 매크로 간접를 사용합니다. 매크로 이름 대신.

따라서 USER##_VS이된다 jack_VS(또는 queen_VS사용자가 설정 한 방식에 따라) USER.

나중에 stringify 매크로가 ( 이 예에서) S(USER_VS)의 값으로 사용되면 해당 값 ( )을 문자열로 변환 하는 간접 단계로 전달됩니다 .USER_VSjack_VSS_(jack_VS)queen"queen"

사용자가 설정 한 경우 USERqueen그 최종 결과는 문자열입니다 "jack".

토큰 연결에 대해서는 https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html을 참조하십시오.

토큰 문자열 변환에 대해서는 https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification을 참조하십시오.

[오타 수정을 위해 2015.02.15 업데이트 됨]


답변

다음은 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


답변

문자열 대신 숫자 값을 사용하십시오.

마지막으로 상수 JACK 또는 QUEEN을 문자열로 변환하려면 문자열 화 (및 / 또는 토큰 화) 연산자를 사용하십시오.


답변

위에서 이미 언급했듯이 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_VSqueen_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매크로가 확장 되지 않았 음을 보여줍니다 .


답변

문자열이 컴파일 시간 상수 인 경우 (귀하의 경우) 다음 트릭을 사용할 수 있습니다.

#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 확장이 아닌 도구 체인 확장 인 것처럼 보이므로이를 고려하십시오.


답변

PatrickJesse 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의 의견에 따르면 내 대답을 수정했습니다.