web-dev-qa-db-ja.com

マクロを使用して関数を宣言していますか?

Cでプリプロセッサマクロを作成することは一般的に推奨または推奨されていませんか?これにより、同じシグネチャ(名前のみが異なる)を持つ多くの関数の作成が容易になります。たとえば、代わりに:

obj *car(const obj *args, obj **envp, GarbageCollector *gc) { ... }
obj *cdr(const obj *args, obj **envp, GarbageCollector *gc) { ... }
obj *cons(const obj *args, obj **envp, GarbageCollector *gc) { ... }
...

あなたができる:

#define DEF_PRIMITIVE(name) obj *name(const obj *args, obj **envp, GarbageCollector *gc)
DEF_PRIMITIVE(car) { ... }
DEF_PRIMITIVE(cdr) { ... }
DEF_PRIMITIVE(cons) { ... }
...

コードの面倒な繰り返しを減らすことがポイントです。これが良いアイデアかどうかについては、どちらの側にもいくつかの議論があります。

これを行う理由

  1. コードの読みやすさの向上
  2. 署名の変更が容易になりました。つまり、これらすべての関数のシグネチャを変更する必要がある場合は、1か所で行う必要があります。 (私の場合、すべての署名を一度変更する必要があり、それは本当に苦痛でした)
  3. このタイプの新しい関数を作成するときに費やされる可能性のあるエラーと時間の削減。

これを行わない理由

  1. これは、関数への引数を覆い隠し、コードに不慣れな人には、関数への引数が何であるか(マクロ定義を見たり見つけたりしない場合)を不明確にします。
  2. 一部のIDEは、これらの定義に問題があり、関数の使用をリファクタリングまたは推論するIDEの機能を阻害しています。
  3. Cプログラムでは通常、マクロを多用することはお勧めしません。マクロのエラーは、通常、バグの特定を困難にします。
3
Jon Deaton

マクロの使用方法は、プログラマーがマクロについて推論することを非常に困難にし、マクロの結果がどうなるかを推測します。最も基本的な例以外では、マクロは通常、エッジのケースとデバッグが非常に困難な問題につながります。

歴史的に、マクロは最適化の目的で有用でした。関数への1回の呼び出しが少ないCPUサイクルを節約することを意味しましたが、これは大きな問題になります。

今日では、関数呼び出しがコードのボトルネックになる場合はほとんどありません。特に 「インライン関数はマクロと同じくらい高速です。」

したがって、いいえ、読みやすさ、メンテナンス、および頭痛の軽減のために、関数の代わりにマクロを使用することは想定されていません。関数には型チェックの利点があり、コードとコンパイラオプションを適宜調整すると、マクロと同じパフォーマンスが得られます。

4