web-dev-qa-db-ja.com

C条件付きプリプロセッサディレクティブの文字列を比較する方法

私はCでこのようなことをしなければなりません。charを使用する場合にのみ機能しますが、文字列が必要です。これどうやってするの?

#define USER "jack" // jack or queen

#if USER == "jack"
#define USER_VS "queen"
#Elif USER == "queen"
#define USER_VS "jack"
#endif
78
frx08

プリプロセッサディレクティブで可変長文字列の比較を完全に行う方法はないと思います。ただし、おそらく次のことができます。

#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コードを使用することもできます。

61
Brian R. Bondy

[更新:2018.05.03]

[〜#〜] caveat [〜#〜]:すべてのコンパイラが同じ方法でC++ 11仕様を実装しているわけではありません。以下のコードは、私がテストしたコンパイラで動作しますが、多くのコメント作成者は異なるコンパイラを使用しました。

Shafik Yaghmourの答えから引用: コンパイル時にC文字列の長さを計算します。これは本当にconstexprですか?

定数式はコンパイル時に評価されることを保証されていません。C++標準のドラフトセクション5.19からこれを言っている定数式以外の引用だけがあります。

[...]> [注:定数式は翻訳中に評価できます。

そのWord canは世界のすべての違いを生みます。

そのため、コンパイラ作成者の仕様の解釈に応じて、constexprを含むこの(または任意の)回答のYMMVを使用します。

[2016.01.31更新]

文字列の比較を必要とせずに目標を達成することでOPの_compile time string compare_アスペクト全体をavoidedしたため、一部の人は私の以前の答えを好まなかったので、詳細な答え。

できません! 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トークンの貼り付けは、明らかではない場合があります。 ;-D

単一の_#_をマクロの前に置くと、マクロはそのままの値ではなく、値の文字列に変更されます。

2つのトークンの間に二重の_##_を挿入すると、それらは単一のトークンに連結されます。

したがって、マクロ_USER_VS_には、USERの設定方法に応じて、_jack_VS_または_queen_VS_という展開があります。

stringifyマクロS(...)はマクロ間接指定を使用するため、名前付きマクロの値は文字列に変換されます。マクロの名前の代わりに。

したがって、USERの設定方法に応じて、_USER##_VS_は_jack_VS_(または_queen_VS_)になります。

後で、stringifyマクロがS(USER_VS)として使用されると、_USER_VS_(この例では_jack_VS_)の値が値(queen)を文字列_"queen"_に変換する間接ステップS_(jack_VS)

USERqueenに設定すると、最終結果は文字列_"jack"_になります。

トークンの連結については、以下を参照してください: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html

トークン文字列の変換については、以下を参照してください: https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification

[タイプミスを修正するために2015.02.15を更新しました。]

20
Jesse Chisholm

文字列の代わりに数値を使用します。

最後に、定数JACKまたはQUEENを文字列に変換するには、stringize(および/またはtokenize)演算子を使用します。

7
Patrick

以下はclangでうまくいきました。シンボリックマクロ値の比較として表示されるものを許可します。 #エラーxxx コンパイラが実際に行うことを確認するだけです。交換 ネコ 定義 #define cat(a、b)a ## b 物事を壊します。

#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プリプロセッサはnot文字列比較をサポートします。ただし、マクロに「反対の値」を割り当てる問題は、「トークンの貼り付け」と「テーブルへのアクセス」で解決できます。 Jesseの単純な連結/文字列化マクロソリューションは、gcc 5.4.0で失敗します。これは、文字列化が行われるためですbefore連結の評価(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)
_

最初の行(macro P_())は、次の行(macro VS())が連結を終了できるように1つの間接参照を追加しますbefore文字列化( を参照)マクロに二重層の間接参照が必要なのはなぜですか? )。文字列化マクロ(S()およびS_())はJesseのものです。

OPのif-then-else構造よりもはるかに簡単に維持できるテーブル(マクロ_jack_VS_および_queen_VS_)はJesseのものです。

最後に、次の4行のブロックは、関数スタイルのマクロを呼び出します。最後の4行のブロックは、ジェシーの答えです。

_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_マクロが文字列化の前にnot展開されていることを示しています。

1
hermannk

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çoisFabreのコメントによると、私は答えを修正しました。

1
benni

文字列がコンパイル時定数である場合(あなたの場合)、次のトリックを使用できます。

#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拡張機能ではなくツールチェーン拡張機能であるように見えるので、考慮に入れてください...

0
EpsilonVector
#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

基本的に、常に終了するnull文字で自動的に初期化される可変長の静的char配列の代わりに、手動で初期化される固定長の静的char配列

0
yan bellavance