2009-11-23 11 views
19

この質問はむしろ基本的なように思えるかもしれませんが、エンジニアリング(コンピュータ科学ではない)の背景から来ているので、 '#'のスニペットがC++コードに含まれているかどうか不安でした。なぜプリプロセッサディレクティブが邪魔になるのですか?

早急に検索すると、プリプロセッサーディレクティブのチュートリアルページcplusplusが簡潔に説明されました。

しかし、なぜプリプロセッサディレクティブの概念には悩まされますか?値を定数に代入し、サブルーチン/関数/マクロを定義し、エラーを処理できる同等のコードを書くことはできませんか?

私は最終的に、そのようなプリプロセッサディレクティブを使用するのがよい習慣であるかどうか、またそうでないときを知りたいと思います。

+11

これは私を楽しませます。 :) – epochwolf

+0

多くの回答で多くの良い点...これらのすべてを消化する時間がかかります! – Zaid

+1

私はこのコミュニティのwikiにすべきでしょうか? – Zaid

答えて

25

実際のアプリケーションの範囲外に何かを行う必要がある場合は、プリプロセッサディレクティブを使用します。たとえば、実行可能ファイルが構築されているアーキテクチャに基づいてコードをインクルードするかどうかを示す前処理が行われていることがわかります。

#ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers 
#include <windows.h> 
#else 
#include <unistd.h> 
#endif 

プリプロセッサディレクティブは、クラス/関数などが複数回定義されないようにするためにも使用されます。

#ifndef MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_ 
#define MY_CLASS_6C1147A1_BE88_46d8_A713_64973C9A2DB7_H_ 
    class MyClass { 
    .... 
    }; 
#endif 

コードやライブラリの中にバージョン管理を組み込むこともできます。 Makefileの

あなたはの線に沿って何か持っている:あなたは

cout << "My application name version: " << MY_APP_VERSION << endl; 
+0

これは、あるプラットフォームには存在するが、PPC上のx86およびVMXオペコードにSSEオペコードをラップする別のコンパイラ組み込み関数などの関数は存在しない場合に特に重要です。この場合、使用する代わりに実行可能な方法はありません#ifdefは、後でそれらを放棄するだけであっても、存在しない関数を使用してテンプレートをコンパイルしようとするためです。 – Crashworks

+0

1つのニックピック:これはデフォルトのプロジェクト設定であるため、WIN32が定義されています。コンパイラ自体は_WIN32を定義しています(MinGWやその他のWindowsベースのコンパイラがこれを定義していると思います)。オプションで_WIN64を使用してプラットフォームを記述します。また、コンパイラのバージョン自体として_MSC_VERを定義します。 – Tom

+0

ええと、申し訳ありませんが別のニックピックです。 '__ [A-Z]'は実装のために技術的に予約されているので、 '__MY_HEADER_H__'は悪いです。ほとんどのコンパイラは*多くの*設定を定義していますので、最終的にこのマクロからの奇妙なマクロ関連のエラーに遭遇する可能性は低いです。 – Tom

7

あなたが書いたコードが実行時に実行されますしながら、プリプロセッサディレクティブは、ビルド時に実行されますので。したがって、プリプロセッサディレクティブは効果的にソースコードをプログラマチックに変更することができます。

Cプリプロセッサは、このようなことのためのかなり粗いメカニズムであることに注意してください。 C++のテンプレートシステムは、コンパイル時にコードを構築するための、より強力なフレームワークを提供します。他の言語には、より強力なメタプログラミング機能(例えば、Lispのマクロシステム)があります。

+3

ちょっとニックする:プリプロセッサディレクティブは、コンパイル時に実行される。 –

+0

@ネマニャ:真。おそらく、私は "実行可能な生成時間"と言いました。違いは、主にコンパイラツールチェーンの実装の詳細です。 –

+1

さて、いいえ、コンパイラがコードを見る前に*プリ*処理する必要があります。 –

9

回答1:条件付きコードは、動作するコンピュータの種類によって異なります。

回答2:コンパイル時に表示される言語拡張機能と互換機能を有効または無効にします。

プリプロセッサはCから来ました。そこには表現できないことがたくさんありました。良いC++コードでは、Cコードよりも使用する理由が少なくなっていますが、残念ながらそれはあまり意味がありません。

+2

また、C++には "インライン"があるので、C++で#defineマクロを使って速度を試してはいけません。 http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Preprocessor_Macros –

+0

@Haroldも参照してください。この回答にインラインが記載されていますか? –

5

それはそれなしで整理するのは難しいだろう二つのもののために最も頻繁に使用されます。

  1. Include guards
  2. さまざまなプラットフォームの異なるコードセクション。
1

いいえ、すべてのケースでプリプロセッサなしでは実際には手に入れることはできません。私のお気に入りのマクロの一つは、私は(おそらく多くの)最終的な実行可能ファイルに無駄なスペースが

そしてまた、あなたが実現しなければならない、C/C++のいずれかの並べ替えを持っていないだろう、マクロなし

#define IFDEBUG if(DEBUG==1) 
//usage: 
IFDEBUG{ 
    printf("Dump of stuff:.,,,"); 
}else{ 
    //..do release stuff 
} 

ですパッケージタイプrequireまたは他のそのようなシステム。プリプロセッサがなければ、コードの重複を防ぐ方法はありません。 (ヘッダファイルはインクルードできません)

+4

これは、最終実行ファイルに "if"テストを残します。#ifdefをDEBUG専用コードの周りに置き、-DDEBUGの有無にかかわらずコンパイルして、printfがプログラムコード内にあるかどうかを制御します。 –

+0

あなたの正しい、私は悪い例を与えた。しかし、私は 'if(0){..}'を最適化するのに十分な十分なコンパイラを使っていたことを祈っています。 – Earlz

+0

悪い例、IMO;単純に「#if defined DEBUG」ではないのですか? – Clifford

0

を持っているコードにそれは外の一部の反射能力を取得するよりも良い何も代替ですが

-D MY_APP_VERSION=4.5.1 

をC++。

同じ名前の変数と文字列を生成するのに非常に便利です。

+0

C++ 0xにベクトルとそのすべてのジャズの反射サポート(ish)はありませんか? – Earlz

+0

いいえ。そして、あなたはベクトルによって何を意味しますか? std :: vector <>は15年ほど前から存在しています。 – Macke

1

しかし、なぜプリプロセッサディレクティブの概念はどうですか?値を定数に代入し、サブルーチン/関数/マクロを定義し、エラーを処理できる同等のコードを書くことはできませんか?

C++での使用は非常に少なく、プリプロセッサに関連する問題を避けるために、言語の機能が作成されています。

私は最終的に、いつプリプロセッサディレクティブを使用するのが良いのかを知りたいと思います。

一般的にC++ソースでは、特に他の言語機能を使用する手段がある場合は、貧弱な方法です。これは、プラットフォーム/ビルドに依存するプログラムや生成プログラムなどのいくつかの場合に必要です。一言で言えば、通常はうまくいきます。 (定数を列挙型として定義するか、マクロの代わりにインラインテンプレートなど)。あなたが1つを使っていると分かっていて、わからない場合は、プリプロセッサを使わずにC++でthis_code_snippetを宣言する方が良いかどうかを尋ねる/尋ねるだけです。

0

Cプリプロセッサはいくつかのタスクを実行しますが、C++ではより良い選択肢がありますが、すべてではありません。 C++には、より良い代替手段があります。これらの選択肢には、#defineマクロの代わりに、テンプレート、インライン展開、およびconst変数(擬似語が標準で呼び出されます)が含まれます。

しかし、あなたがしなくてはいけない、あるいは単になしではできないことはほとんどありません。たとえば、#includeは必須であり、複数のプラットフォームまたは構成をコーディングする場合、条件付きコンパイルは有用です(すべてのケースで控えめに使用する必要があります)。

#pragmaによって制御されるコンパイラ固有の拡張は、場合によっては避けられない場合があります。

+0

うん... '#include'と' #pragma'が他の方法で書かれる方法を想像できません。 – Zaid

+0

すべてのコンテンツを1つのファイルにコピーすることで#includeを避けることができます! ;)それが悪いことではない、良いことである理由です。 #pragmaは、移植性の理由から、#pragmaに頼らなければならない場合でも悪いままです。 – Clifford

+1

...またはこの40年前のプリプロセッサのハックに頼るのではなく、他のコンパイルユニットへのインタフェースを扱うための適切な言語構造を作成する。 (実際のインターフェースの適切な動作を得るために、すべての人がインクルードされたファイルにすべてを入れなければならない余分な '#ifdef'を忘れないようにしましょう)。 –

6

コードをコンパイルする前に前処理が行われます。

#ifdef WIN32 
#include <something.h> 
#elseif LINUX 
#include <somethingelse.h> 
#endif 

実行時ではなくコンパイル時に実行したいヘッダファイルを明示的に含めます。変数でこれを行うことはできません。一方、

一方、C++では、それは良い習慣であると大幅次の例

#define PI 3.141592654 
with 
const double PI=3.141592654; 

のような定数式を交換することを奨励理由は、あなたが適切な型キャストとデータ型の取り扱いを得るということです。

また

#define MAX(x,y) (x) > (y) ? (x) : (y) 

あなたはプリプロセッサがでていることを代わる

int i = 5 
int max = MAX(i++, 6); 

を書くことができますので、非常に良いではありません。

明確に意図を与えるつもりはない
int max = (i++) > (6) ? (i++) : (6); 

結果。

代わりに、MAXは関数でなければなりません(マクロではありません)。関数の場合は、パラメータに型を指定することもできます。

私はあらゆる種類の興味深いものに使用されているプリプロセッサを見てきました。言語のキーワード宣言と同様。この場合、読みやすくすることができます。

要するに、条件付きインクルード指示などのコンパイルタイプで発生する必要のあるものに対しては、プリプロセッサを使用します。定数の使用は避けてください。可能な限りマクロを避け、機能を使用する。

+0

プラットフォーム間の互換性のために行く必要はないだろうが、私はその絵を手に入れた。 – Zaid

+0

max()は既にC++の標準ライブラリに入っていますが、すべてうまくテンプレート化されています。 – ceo

2

一般に、プリプロセッサディレクティブは使用しないでください。悲しいことですが、時にはCとC++にする必要があります。

Cはもともと、プリプロセッサを使用せずに真剣に何もできないような方法で言語を定義しました。言語には、モジュラープログラム、定数、インラインコードの作成、または汎用プログラミングのためのサポートが組み込まれていませんでした。

C++はこれらの問題のほとんどを取り除きますが、施設はまだ存在しているので、まだ使用されています。 (興味深いことに、モジュール化されていない、まだ#includeに固執しています)、

プリプロセッサを持たない同様のタスクで、同様の抽象度で構築された言語と比較したい場合は、 Adaをご覧ください。

2

多くのプログラミング言語ではなく、ランタイム環境よりも、フォローするあなたがコンパイラためのコードを書くメタプログラミング施設を、持っています。

たとえば、C++では、型、またはコンパイル時定数に基づいて特定のコードを生成するようにコンパイラに指示するためのテンプレートが用意されています。 Lispはおそらく高度なメタプログラミング機能を持つ言語の最も有名な例です。

Cプリプロセッサディレクティブ/マクロは、他の言語で使用可能なものよりも比較的粗い形式ですが、「メタプログラミング」の単なる別の形式です。プリプロセッサディレクティブは、特定のプラットフォームで特定のコードを無視する、コード内の文字列を見つけて別の文字列で置き換えるなど、コンパイル時に特定の処理を実行するようにコンパイラに指示します。これは、コードがすでにコンパイルされた後、実行時に行うことは不可能です。

本質的に、Cプリプロセッサは初期の「メタプログラミング」つまりコンパイラプログラミングです。

+0

ええと...前にメタプログラミングについて聞いたことはありません。私はプリプロセッサディレクティブの使用法を一般化したのが好きです。 – Zaid

1

C++はCから開発されました.C++よりもはるかに多くのプリプロセッサが必要でした。たとえば、C++で定数を定義するには、#define FOO 4の代わりに、const int foo = 4;のようなものを書きます。残念ながら、あまりにも多くの人々がプリプロセッサの習慣をCからC++に持ってきました。

C++のプリプロセッサには、いくつかの妥当な用途があります。ヘッダファイルに#includeを使用することはかなり必要です。ヘッダーインクルードガードを含む条件付きコンパイルにも便利なので、ヘッダーを複数回(例えば異なるヘッダーで)#includeにして、1度しか処理しないようにすることができます。 assertステートメントは、実際にはプリプロセッサマクロであり、同様の用途がいくつかあります。

これ以外にも、C++で正当な使用法はほとんどありません。

+0

'#include 'と関連する' #ifdef ... #define'は、Pascal、Modula-2、Adaに見られるようにCもC++も適切なインタフェース機能を作成するのに適していないので、ほぼすべての現代語。これは、誰もが慣れてきたプリプロセッサのハックです。 –

関連する問題