web-dev-qa-db-ja.com

マクロでの整数定数式の検出

Linuxカーネルのメーリングリストで、引数が整数定数式であり、整数定数式であるかどうかをテストするマクロに関する議論がありました。

Martin Ueckerによって提案されたglibcのtgmath.h から インスピレーション を取得)を使用しない、特に巧妙なアプローチの1つは次のとおりです。

#define ICE_P(x) (sizeof(int) == sizeof(*(1 ? ((void*)((x) * 0l)) : (int*)1)))

このマクロは、引数が整数定数式の場合は値1の整数定数式に展開され、そうでない場合は0に展開されます。ただし、許可されるのはsizeof(void)に依存しています(sizeof(int)とは異なります)。これは GNU C拡張 です。

組み込みなしで、また言語拡張機能に依存せずに、そのようなマクロを書くことは可能ですか?はいの場合、引数を評価しますか?


上記のマクロの説明については、代わりに以下を参照してください: Linux Kernelの__is_constexprマクロ

35
Acorn

?:式の型は、引数がNULLポインター定数か通常のvoid *かによって異なりますが、 _Generic で型を検出するという同じ考え方を使用します。

#define ICE_P(x) _Generic((1? (void *) ((x)*0) : (int *) 0), int*: 1, void*: 0)

Ideoneのデモ_GenericはC11の追加であるため、C99またはそれ以前のものにこだわっている場合は使用できません。

また、 nullポインター定数の定義 および nullポインター定数が?:式の型と対話する方法 の標準リンクがあります。

値が0の整数定数式、またはvoid *型にキャストされたそのような式は、nullポインター定数と呼ばれます。

そして

2番目と3番目のオペランドの両方がポインターであるか、1つがNULLポインター定数で、もう1つがポインターである場合、結果の型は、両方のオペランドが参照する型のすべての型修飾子で修飾された型へのポインターです。さらに、両方のオペランドが互換型または互換型の異なる修飾バージョンへのポインターである場合、結果の型は複合型の適切に修飾されたバージョンへのポインターです。 一方のオペランドがNULLポインター定数の場合、結果にはもう一方のオペランドの型が含まれます。それ以外の場合、1つのオペランドはvoidまたは修飾バージョンのvoidへのポインターです。この場合、結果の型は適切に修飾されたバージョンのvoidへのポインターです。

25
user2357112

sizeof(void)が標準ではないという修正はありませんが、次のようなことを行うことでsizeof(void) == sizeof(int)という可能性を回避できます。

#define ICE_P(x) ( \
  sizeof(void) != \
  sizeof(*( \
    1 ? \
      ((void*) ((x) * 0L) ) : \
      ((struct { char v[sizeof(void) * 2]; } *) 1) \
    ) \
  ) \
)

私はそれが完全な答えではないことを知っていますが、わずかに近いです...

Edit:どのソリューションがさまざまなコンパイラーで機能するかについて、少し調査しました。 Hedley ;で次の情報をすべてエンコードしました。 HEDLEY_IS_CONSTANTHEDLEY_REQUIRE_CONTEXPR、およびHEDLEY__IS_CONSTEXPRマクロを参照してください。パブリックドメインであり、ヘッダーが1つなので、プロジェクトに簡単にドロップするか、興味のある部分をコピーすることができます。

C11マクロとバリアント

user2357112のC11マクロshouldanyC11コンパイラで動作しますが、 SunCC および PGI は現在壊れているため、ブラックリストに登録する必要があります。また、IARは__STDC_VERSION__をC++モードで定義しますが、このトリックはC++では機能しません(AFAIK nothing does )。したがって、おそらく__cplusplus isn定義されていません。 GCC、clang(およびemscriptenのようなclangから派生したコンパイラー)、ICC、IAR、XL C/C++で実際に動作することを確認しました。

それ以外に、一部のコンパイラは、古いモードでも拡張として_Genericをサポートしています。

  • GCC 4.9 +
  • clang; __has_feature(c_generic_selections)で確認します(ただし、-Wc11-extensions警告を無効にすることもできます)
  • ICC 16.0 +
  • XL C/C++ 12.1+

また、intvoid*にキャストすると、コンパイラが警告を発する場合があることに注意してください。これを回避するには、最初にintptr_tthenにキャストしてvoid*にキャストします。

#define ICE_P(expr) _Generic((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)

または、__INTPTR_TYPE__を定義するコンパイラ(GCCなど)では、intptr_tの代わりにそれを使用でき、stdint.hを含める必要はありません。

ここで可能な別の実装は、__builtin_types_compatible_pの代わりに_Genericを使用することです。私はそれが機能するコンパイラを認識していませんが、元のマクロは機能しませんが、-Wpointer-arith警告から抜け出します:

#define IS_CONSTEXPR(expr) \
  __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)

このバージョンは、3.1に戻るGCC、およびclangやICCなどの≥3.1を示す値に__GNUC__/__GNUC_MINOR__を定義するコンパイラで動作するはずです。

この回答のマクロ

sizeof(void)をサポートするコンパイラはすべて動作しますが、警告(-Wpointer-arithなど)に遭遇する可能性は十分にあります。そうは言っても、dosizeof(void)をサポートするAFAICTコンパイラーは常にサポートしているようです。そのため、これらのコンパイラーのanyバージョン動作するはずです:

  • GCC
  • Clang(および組み込みのコンパイラ、__clang__も定義)
  • ICC(テスト済み18.0)
  • XL C/C++(テスト済み13.1.6)
  • TI(テスト済み8.0)
  • TinyCC

__builtin_constant_p

ユースケースによっては、__builtin_constant_pをサポートするコンパイラーで使用することをお勧めします。これは、整数定数式よりも少し一般的(そして曖昧)です。コンパイラーがコンパイル時に値を知っているというだけです。これらのコンパイラはそれをサポートすることが知られています:

  • GCC 3.1+
  • クラン
  • ICC(テスト済み18.0)
  • TinyCC 0.9.19+
  • armcc 5.04以降
  • XL C/C++(文書化されていませんが、13.1.6 +で確実に動作します)

マクロを使用して、コンパイル時に値を知っているが実行時に遅い場合にコンパイラーが一定に折りたたむことができるコードパスと、コンパイラーに対してブラックボックスであるが実行時に速いコードパスを選択する場合、 __builtin_constant_pを使用します。

OTOH、値が本当に標準に従ってICEであることを確認する場合は、__builtin_constant_pを使用しないでください。例として、exprがICEの場合はexprを返し、そうでない場合は-1を返すマクロを次に示します。

#if defined(ICE_P)
#  define REQUIRE_ICE(expr) (ICE_P(expr) ? (expr) : (-1))
#else
#  define REQUIRE_ICE(expr) (expr)
#endif

次に、VLAを使用する場合にコンパイラーがエラーを表示する場合、マクロで配列を宣言するときにそれを使用できます。

char foo[REQUIRE_ICE(bar)];

とはいえ、GCCとclangはどちらも-Wvla警告を実装しており、代わりに使用することもできます。 -Wvlaの利点は、ソースコードの変更を必要としないことです(つまり、char foo[bar];と書くだけでよい)。欠点は、それほど広くサポートされていないことと、 適合配列パラメーター を使用しても診断がトリガーされることです。そのため、多くの誤検知を避けたい場合は、このマクロが最善の策です。

何もサポートしていないコンパイラ

  • MSVC
  • DMC

アイデア歓迎:)

18
nemequ