6

C/C++ の悪い、または危険な関数のようなマクロの例には事欠きません。

#define SQUARE(x) x * x
printf("%d", SQUARE(4));   //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE

#define SQUARE(x) (x) * (x)
printf("%d", 36/4);                //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE

#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16

int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE

関数のようなマクロの問題を考えると、それらの適切な/賢明な/推奨される使用例は何ですか?

関数のようなマクロが関数よりも好ましい場合はありますか?

これには本当に良いケースがいくつかあるにちがいないと思います。

4

6 に答える 6

3

コメントはそのほとんどをキャプチャしました。

一部のタイプのデバッグとインストルメンテーションは、関数スタイルのマクロを使用することによってのみ実行できます。最適な例は ですがassert()、プロファイリング用のコードを計測するために関数スタイルのマクロを使用することもできます。__FILE____LINE__のような魔法のマクロや#、引用や##トークンの貼り付けなどの機能により、関数のようなマクロはデバッグやプロファイリングに役立ちます。

C++ では、テンプレートと通常より積極的なインライン展開を使用するため、関数スタイルのマクロを使用する他の理由はほとんどありません。たとえば、テンプレート関数は、質問に示されている理由からstd::max、マクロよりもはるかに優れたソリューションです。MAX

C では、コードの小さな断片を確実にインライン化することが最適化で必要になる場合があります。マクロは、すべての注意事項がありますが、このコンテキストではまだ時々役に立ちます。ALLCAPS 命名規則は、単純なテキスト置換に関するすべての問題があるため、これは実際には関数ではなくマクロであることをプログラマーに警告するためにあります。たとえばstd::max、パフォーマンスが重要なコードの一部で に相当するものが必要な場所がいくつかある場合、マクロは (すべての危険を伴う) 有用なソリューションになる可能性があります。

于 2012-09-09T18:37:02.253 に答える
2

ほとんどの場合、マクロを使用する必要はありません。ただし、正当な場合もあります (議論の余地はありますが)。

列挙型を使用できる場合は、マクロを使用しないでください。マジック ネームを持つローカル変数を持つことに依存するマクロは使用しないでください。左辺値として使用できるマクロは使用しないでください。展開されたマクロの周りのコードに副作用があるマクロは使用しないでください。インライン関数の代わりにマクロを使用することは、ほとんどの場合、悪い考えです。いずれにせよ、リストは無限にあります。

ただし、マクロを使用して反復子を偽造することもできますが、C および特に Linux カーネルでは、次のように表示されます。

#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
     pos = n, n = pos->next)

Linux カーネル ソース全体で使用される同様のタイプのマクロが他にも多数あります。

また、offsetof(3) は通常マクロとして実装され、assert(3) などと同様です。

于 2012-09-09T18:34:55.263 に答える
1

I generally agree with dmp here. If you can use something else you use it and forget about macros.

That said I will mostly repeat what's in comments:

  1. You usually need function like macro when you need to use inside it some other preprocessor symbols (__LINE__, __FILE__, __func__ and so on). This usually means assert/debugging macros.
  2. Stringizing / pasting tokens (# and ## operators) in a macro.
  3. You CAN (if you feel adventures enough) use function-like macros to create enum->string mappings using some cleaver tricks like defining/undefining and redefining macros.
  4. Something like va_list/va_arg where you need access through pointer with simultaneous change of the place where it points to.

And I repeat myself - if you can avoid preprocessor just do that.

于 2012-09-09T18:48:01.937 に答える
0

Google Style C++ Guideには、非常に便利なマクロの例が 1 つあります (これが「関数のようなマクロ」の意味かどうかはわかりません。引数を取りますが、関数呼び出しに置き換えることはできません)。

DISALLOW_COPY_AND_ASSIGN(クラス名);

このようなマクロをクラスのプライベート セクションに配置して、コンパイラによって自動的に生成されるコピー コンストラクターと代入演算子を無効にすることができます。通常、これら 2 つの自動生成メソッドを、クラスが実際に必要としない限り許可しないことをお勧めします。マクロを無効にしないと、すべてのクラスで非常に多くの入力が必要になります。

ClassName(const ClassName&);
void operator=(const ClassName&);

マクロは、このコードを自動的に生成します。これはかなり安全です。そのようなマクロが問題を引き起こすケースを私は知りません。

于 2012-09-09T20:28:03.217 に答える
0

C標準にはそれらがたくさんあります(または許可されています)。同じ精神でマクロを行うものはすべて正当であるべきだと思います:

  • 文字処理関数isspaceなどはマクロとして実装されることが多い
  • CMPLXマクロとして宣言されています
  • UINT64_C同様のものはマクロです
  • tgmath.hsin、および他の多くの関数を含めると、cosマクロになります
  • new_Genericキーワードの要点は、関数のようなマクロを書くことです
于 2012-09-09T18:44:38.943 に答える
0

場合によっては、コンパイラーごとに異なり、書き換えが煩雑なコンパイラー固有の命令が存在します。このカテゴリに分類される私が頻繁に使用する 1 つのマクロは、パラメーターが使用されていないことをコンパイラーに通知するために使用されます。

virtual size_t heightForItemAtIndex(const size_t& idx) const {
  MONUnusedParameter(idx); // << MONUnusedParameter() is the macro
  return 12; // << this type's items are all the same height
}

問題はここでよりよく紹介されています:使用されていない変数の警告をサイレンシングするためのクロス プラットフォーム マクロ

于 2012-09-09T18:45:20.237 に答える