プリプロセッサディレクティブまたはif(定数)ステートメントを使用する方が良いですか?


10

多くの異なる衣装に使用されるコードベースがあり、タイプXの衣装にのみ関連するいくつかのコードがあるとします。このコードをタイプXの衣装にのみ含めるには、プリプロセッサディレクティブを使用する方が良いですか、またはifステートメントを使用するには?より明確にするために:

// some code
#if TYPE_X_COSTUMER  = 1
// do some things
#endif
// rest of the code

または

if(TYPE_X_COSTUMER) {
    // do some things
}

私が考えることができる引数は次のとおりです。

  • プリプロセッサディレクティブにより、コードフットプリントが小さくなり、ブランチが少なくなります(最適化されていないコンパイラでは)
  • ステートメントが常にコンパイルされるコードで発生する場合、たとえば、誰かが間違いを犯して、彼が取り組んでいるプロジェクトの無関係なコードに悪影響を与える場合でも、エラーは表示され、コードベースは破損しません。そうでなければ、彼は腐敗に気づかないでしょう。
  • 私は常に、プリプロセッサーの使用よりもプロセッサーの使用を優先するように言われました(これがまったく議論の場合...)

何が望ましいですか-多くの異なる衣装のコードベースについて話すとき?


3
最適化コンパイラを使用して作成されていないビルドを出荷する確率はどれくらいですか?そして、それが起こった場合、影響は問題になりますか?(特に、他のすべての最適化との関連で見逃してしまうでしょう)?

@delnan-コードは、さまざまなコンパイラを備えたさまざまなプラットフォームで使用されます。そして影響に関して-それは特定のケースであるかもしれません。
MByD 2011年

あなたは何かを好むように言われましたか?それは、誰かが鶏肉が好きではないのに、KFCからの食品を強制的に食べるようなものです。
右折

@WTP-いいえ、私はそうではありませんでした。もっと知りたいので、将来はより良い決断を下すでしょう。
MByD 2011年

2番目の例では、TYPE_X_CUSTOMERまだプリプロセッサマクロですか?
detly

回答:


5

あなたが言及しなかった#defineを使用することの1つの利点があると私は思います、そしてそれはコマンドラインで値を設定できるという事実です(したがって、ワンステップのビルドスクリプトから設定します)。

それ以外は、一般的にマクロを避ける方が良いです。彼らはスコープを尊重せず、それが問題を引き起こす可能性があります。非常に愚かなコンパイラだけが、コンパイル時定数に基づいて条件を最適化できません。それがあなたの製品の懸念事項かどうかはわかりません(たとえば、組み込みプラットフォームではコードを小さく保つことが重要かもしれません)。


実際には、これ組み込みシステム用です
MByD

問題は、組み込みシステムが通常いくつかの古いコンパイラ(たとえばgcc 2.95など)を使用していることです。
BЈовић

@VJo-GCCはラッキーケースです:)
MByD

それを試してみてください。未使用のコードが含まれていて、それがかなりのサイズである場合は、定義に進んでください。
タマシュSzelei

コンパイラーのコマンドラインから設定できるようにする場合でも、コード自体で定数を使用し、その定数に割り当てられた値のみを#defineします。
CodesInChaos 2014年

12

多くの質問については、この質問の答えはそれが依存するということです。どちらが良い言う代わりに、私はどちらかが他より優れている例と目標を与えました。

プリプロセッサと定数の両方には、適切な使用法の独自の場所があります。

プリプロセッサの場合、コードはコンパイル前に削除されます。したがって、コードがコンパイルされないことが予想される状況に最適です。これは、モジュールの構造、依存関係に影響を与える可能性があり、パフォーマンスの観点から最適なコードセグメントを選択できる場合があります。以下の場合、プリプロセッサのみを使用してコードを分割する必要があります。

  1. マルチプラットフォームコード:
    たとえば、コードが異なるプラットフォームでコンパイルされた場合、コードがOSの特定のバージョン番号(またはコンパイラのバージョン)に依存している場合(これは非常にまれです)。たとえば、リトルエンディアンとビッグエンディアンの対応するコードを扱う場合、定数ではなくプリプロセッサを使用して分離する必要があります。または、WindowsとLinuxのコードをコンパイルしていて、特定のシステムコールが大きく異なる場合。

  2. 実験的なパッチ:
    これが正当化するもう1つのケースは、リスクのある実験的なコード、または大幅なリンクまたはパフォーマンスの違いがある省略する必要がある特定の主要なモジュールです。if()の下に隠すのではなく、プリプロセッサを介してコード無効にしたい理由は、この特定の変更セットによって導入されたバグを確信できない可能性があり、実験的に実行しているためです。失敗した場合は、本番環境でそのコードを無効にする以外に何もする必要はありません。コード全体をコメント化するのに使用するのが理想的な場合もあります。#if 0

  3. 依存関係の処理:
    生成する可能性のあるもう1つの理由たとえば、JPEG画像をサポートしたくない場合は、そのモジュール/スタブのコンパイルをやめると、最終的にライブラリが(静的または動的に)リンクしなくなります。モジュール。./configureそのような依存関係の可用性を特定するためにパッケージが実行される場合があり、ライブラリが存在しない場合(またはユーザーが有効にしたくない場合)、そのライブラリとリンクしないと、そのような機能は自動的に無効になります。ここで、これらのディレクティブが自動生成される場合は常に有益です。

  4. ライセンス:
    プリプロセッサディレクティブの例で非常に興味深いのはffmpegです。それはその使用によって特許を侵害する可能性のあるコーデックを持っています。ソースをダウンロードしてコンパイルしてインストールする場合、そのようなものを使用するかどうかを尋ねられます。条件がまだあなたを法廷に着地させることができるならば、いくつかの下でコードを隠しておく!

  5. コードのコピーと貼り付け:
    別名マクロ。これは、マクロを使いすぎることへのアドバイスではありません。マクロには、コピーペーストを同等に適用するためのより強力な方法があります。ただし、慎重に使用してください。そして、あなたがあなたが何をしているか知っているなら、それを使ってください。もちろん定数はこれを行うことができません。しかしinline、それが簡単であれば、関数を使用することもできます。

では、いつ定数を使用しますか?
ほとんどどこでも。


  1. より適切なコードフロー:一般に、定数を使用する場合、定数は通常の変数とほとんど区別がつかないため、コードが読みやすくなります。75行のルーチンを作成する場合は、#ifdefを使用して10行ごとに3行または4行を置いてください。おそらく#ifdefによって管理される一次定数が与えられ、どこでも自然な流れの中でそれを使用します。

  2. 適切にインデントされたコード:すべてのプリプロセッサディレクティブは、適切にインデントされたコードではうまく機能しません。場合でも、あなたのコンパイラが#defのインデントを許可しない、プリANSI Cプリプロセッサは、ラインの開始と「#」の文字の間にスペースを許可しませんでした。先頭の「#」は常に最初の列に配置する必要がありました。

  3. 構成:
    定数/または変数が理にかなっているもう1つの理由は、定数または変数がグローバルにリンクされていることから簡単に進化できること、または将来的に構成ファイルから派生するように拡張できることです。

最後に 、スコープをまたぐためにプリプロセッサディレクティブを
使用しないでください。つまり、の異なる側の開始または終了。これは非常に悪いです。混乱を招く可能性があり、危険な場合もあります。#ifdef#endif{ ... }#ifdef#endif{ ... }

もちろん、これは完全なリストではありませんが、どの方法がより使いやすいかという明確な違いを示しています。どちらが優れているかということではなく、特定のコンテキストでどちらがより自然に使用できるかは常により重要です。


長く詳細な回答をありがとう。私はあなたが言及したほとんどのことを知っていましたが、質問はプリプロセッサ機能を使用するかどうかではなく、特定のタイプの使用法に関するものであるため、質問から省略されました。しかし、あなたがした基本的な点は本当だと思います。
MByD '25年

1
「プリプロセッサディレクティブ#ifdefから#endifをスコープまたは{...}を超えて使用しない」は、IDEに依存します。IDEが非アクティブなプリプロセッサブロックを認識してそれらを折りたたむか、別の色で表示する場合は、混乱しないでしょう。
Abyx 2011

@Abyx IDEが役立つことは事実です。ただし、人々が* nix(linuxなど)プラットフォームで開発する多くの場合-エディターなどの選択肢はたくさんあります(VI、emacsなど)。そのため、他の誰かがあなたとは異なるエディターを使用している可能性があります。
Dipan Mehta

4

あなたの顧客はあなたのコードにアクセスできますか?はいの場合、プリプロセッサの方が適している可能性があります。同様のものがあり、さまざまな顧客(または顧客固有の機能)にコンパイル時フラグを使用しています。次に、スクリプトで顧客固有のコードを抽出し、そのコードを出荷します。顧客は、他の顧客や他の顧客固有の機能を認識していません。


3

注目に値するいくつかの追加ポイント:

  1. コンパイラは、プリプロセッサでは処理できない、ある種の定数式を処理できます。たとえば、プリプロセッサは通常sizeof()、非プリミティブ型を評価する方法がありません。これにより、の使用が強制さif()れる場合があります。

  2. コンパイラーは、スキップされたコードのほとんどの構文問題を無視します#if(一部のプリプロセッサー関連の問題は依然として問題を引き起こす可能性があります)が、でスキップされたコードの構文の正確さを主張しif()ます。(例えば、識別子を改称)、いくつかのためにスキップされたコードのビルドではなく、他の人が別の場所にファイルの変更の結果として無効となった場合このように、それは一般的に、スコーク、それが持つ無効だ場合は、すべてのビルドになりますif()ではなく、それはでは無効になっていた場合#if。コードがスキップされる理由によって、それは良いことかもしれませんし、そうでないかもしれません。

  3. 一部のコンパイラは到達できないコードを省略しますが、他のコンパイラは省略しません。ただし、到達不能コード内の静的ストレージ期間の宣言(文字列リテラルを含む)は、割り当てられたスペースをコードが使用できない場合でも、スペースを割り当てる可能性があります。

  4. マクロはif()それらの中でテストを使用できますが、プリプロセッサがマクロ内で条件付きロジックを実行するメカニズムはありません[マクロ内で使用する場合、? :演算子はしばしばより優れているかもしれませんifが、同じ原理が適用されます]。

  5. この#ifディレクティブを使用して、定義するマクロを制御できます。

if()ほとんどの場合、コンパイラーが使用しているもの、または定義する必要のあるマクロに基づいて決定を行う場合を除いて、多くの場合はよりクリーンであると思います。上記の問題の中には、よりクリーンに見える#if場合でも、の使用が必要になる場合がありifます。


1

簡単に言えば、プリプロセッサディレクティブに対する恐れは、基本的に2つ、複数のインクルード、そしておそらくより読みにくいコードとより不可解なロジックです。

プロジェクトが小さく、将来の大きな計画がほとんどない場合、両方のオプションで賛否両論の比率は同じになると思いますが、プロジェクトが巨大になる場合は、別のヘッダーファイルを作成するなど、別のことを検討してください。それでもプリプロセッサディレクティブを使用する必要がありますが、この種のアプローチは通常、コードを読みにくくし、その定数の名前がプログラム自体のビジネスロジックにとって意味があることを忘れないでください。


これはすばらしいコードベースであり、定義は確かにいくつかの別のヘッダーにあります。
MByD
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.