web-dev-qa-db-ja.com

プリプロセッサシンボルが#defineされているが値がないかどうかをテストする方法は?

C++プリプロセッサディレクティブを使用して、プリプロセッサシンボルが定義されているが値がないかどうかをテストできますか?そんな感じ:

_#define MYVARIABLE
#if !defined(MYVARIABLE) || #MYVARIABLE == ""
... blablabla ...
#endif
_

編集:これを実行している理由は、現在取り組んでいるプロジェクトは/DMYSTR=$(MYENVSTR)を介して環境から文字列を取得することになっており、この文字列が空である可能性があるためです。ユーザーがこの文字列を定義し忘れた場合、プロジェクトがコンパイルに失敗することを確認したいと思います。

36
galets

相馬マクロマジック:

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL)     DO_EXPAND(VAL)

#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)

Only here if MYVARIABLE is not defined
OR MYVARIABLE is the empty string

#endif

コマンドラインでMYVARIABLEを定義する場合、デフォルト値は1です。

g++ -DMYVARIABLE <file>

ここで、MYVARIABLEの値は1です。

g++ -DMYVARIABLE= <file>

ここで、MYVARIABLEの値は空の文字列です

解決された引用問題:

#define DO_QUOTE(X)        #X
#define QUOTE(X)           DO_QUOTE(X)

#define MY_QUOTED_VAR      QUOTE(MYVARIABLE)

std::string x = MY_QUOTED_VAR;
std::string p = QUOTE(MYVARIABLE);
41
Martin York

この問題の解決策を見たことがありませんが、一般的に使用されていないことに驚いています。 Xcode Objcで動作するようです。 「値なしで定義」と「定義セット0」を区別する

#define TRACE
#if defined(TRACE) && (7-TRACE-7 == 14)
#error TRACE is defined with no value
#endif
10
Ian Brackenbury

ユーザーがこの文字列を定義し忘れた場合、プロジェクトがコンパイルに失敗することを確認したいと思います。

前のビルドステップでこれをチェックしますが、コンパイル時にこれを行うことができます。簡潔にするためにBoostを使用する:

#define A "a"
#define B
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(A)) > 1); // succeeds
BOOST_STATIC_ASSERT(sizeof(BOOST_STRINGIZE(B)) > 1); // fails
10
Georg Fritzsche

Mehradの答えは、それを機能させるために拡張する必要があります。彼のコメントも

/ * MYVARI(A)BLEはここでは未定義です* /

不正解です。未定義の変数をテストするには、単純なテスト_#ifndef MYVARIABLE_があります。

しかし、そのようなテストの後、彼の表現は元の質問の正しい解決につながります。マクロMYVARIABLEの未定義、定義済みだが空、および空ではない値に対して、このコードが機能することをテストしました。

_#ifndef MYVARIABLE
    /* MYVARIABLE is undefined here */
#Elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
    /* MYVARIABLE is defined with no value here */
#else
    /* MYVARIABLE is defined here */
#endif
_

_#Elif_ステートメント~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1は次のように機能します。

  • MYVARIABLEが定義されているが空の場合、~(~+0) == 0 && ~(~+1) == 1に展開され、_0==0 && 1==1_になります(二重否定~~はID演算子です)。
  • MYVARIABLEが数値(nなど)に定義されている場合、~(~n+0)==0 && ~(~n+1)==1に展開されます。 _&&_の左側では、式~(~n+0)==0は_n==0_に評価されます。しかし、_n==0_の場合、右側は~(~0+1)==1に評価され、〜0は-1から~(-1+1)==1になり、次に_~0==1_、最後に_-1==1_になります。 、これは明らかに誤りです。
  • MYVARIABLEが数値以外の値に定義されている場合、プリコンパイラーはすべての不明なシンボルを0に減らし、n == 0の前のケースをもう一度取得します。

私の完全なテストコード(test.cファイルとして保存):

_#include <stdio.h>

int main() {
    printf("MYVARIABLE is "
#ifndef MYVARIABLE
     "undefined"
#Elif ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
     "defined without a value"
#else 
     "defined with this value : %i", MYVARIABLE
#endif
    );
    printf("\n");
}
_

GNUプリプロセッサcppを使用すると、どのコードが生成されるかを実験して確認できます。

_# undefined
cpp test.c
#defined without a value
cpp -DMYVARIABLE= test.c
#defined wit an implicit value 1
cpp -DMYVARIABLE test.c
#defined wit an explicit value 1
cpp -DMYVARIABLE=1 test.c
#defined wit an explicit value a
cpp -DMYVARIABLE=a test.c
_

またはコンパイルと実行の出力(一部のLinuxの場合)

_$ gcc -o test test.c ; ./test
MYVARIABLE is undefined
$ gcc -DMYVARIABLE= -o test test.c ; ./test
MYVARIABLE is defined without a value
$ gcc -DMYVARIABLE -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=1 -o test test.c ; ./test
MYVARIABLE is defined with this value : 1
$ gcc -DMYVARIABLE=a -o test test.c ; ./test
test.c: In function ‘main’:
<command-line>:0:12: error: ‘a’ undeclared (first use in this function)
...
_

MYVARIABLEが 'a'として定義されている最後の実行では、エラーはマクロ定義のエラーではありません。マクロは、「この値で定義された...」という最後のケースを正しく導きます。しかし、この値は「a」であり、「a」はコードで定義されていないため、コンパイラまたはコースはこれを通知する必要があります。

このように、最後のケースは、元の質問の意図が非常に危険である理由の非常に良い例です。マクロを使用して、ユーザーはコンパイルするコードにプログラム行のシーケンスを導入できます。このようなコードが導入されていないことを確認するには、有効な値でマクロをさらに多く確認する必要があります。このタスクを前処理に任せる代わりに、おそらく完全なスクリプトが必要です。そしてその場合、前処理でそれをチェックすることの用途は何ですか?

5
db-inf

BOOST_PP_IS_EMPTYマクロは次のようになります:

#include <boost/preprocessor/facilities/is_empty.hpp>

#define MYVARIABLE
#if !defined(MYVARIABLE) || !BOOST_PP_IS_EMPTY(MYVARIABLE)
    // ... blablabla ...
#endif

それは私にとってはトリックでした。このマクロは文書化されていないので、追加するつもりですので、注意して使用してください。

ソース: preprocessor-missing-IS-EMPTY-documentation

3
mister why

これができるとは思いません。そうは言っても、私はそれの必要性を見ていない。プリプロセッサ#defineシンボルを作成するときは、#ifで使用するために1または0として定義するか、空白のままにする規則を確立する必要があります。

2
Reinderien

プリプロセッサは数値のみをチェックできるため、これはできません。文字列比較はプリプロセッサ構文ではカバーされていません。

2
harper

#if MYVARIABLE==0私の答えは少なくとも30文字でなければならないので、それで十分です!

0
Kris Morness

整数のみのマクロの場合...

追加のマクロなしでハックを使用できます:

#if ~(~MYVARIABLE + 0) == 0 && ~(~MYVARIABLE + 1) == 1
/* MYVARIBLE is undefined here */
#endif
0
user541686