2017-05-31 12 views
0

次のコードは、g ++ -std = C++ 11コンパイラによるコンパイルに失敗します。C++で異なる数の引数を持つネストされたマクロを使用する

# include<iostream> 
    # include<vector> 

    using namespace std; 


    # define stlf(x)  x.begin(), x.end() 
    # define repf(it, a, b) for(auto it = a ; it != b ; ++it) 


    /* 
    // Also, following alternative fails 

    # define repf(it, a, b) for(auto it = a ; it != b ; ++it) 
    # define stlf(x)  x.begin(), x.end() 

    */ 



    typedef vector<int > vi; 

    # define pd(x) printf("%d", x); 

    int main(void){ 

     vi arr(10, -1); 

     repf(arr, stlf(arr)) 
      pd(arr[i]); 


     return 0; 
    } 

1.なぜこの出来事はありますか?

2.彼らは、この機能を回避することを、C++プリプロセッサの実装のための実装上の問題だったかもしれない何?

どのようにしてこのようなショートカットを使用できますか?

+0

あなたはこのような「ショートカット」を避けるためにうまくいくでしょう。誰かがあなたのコードを読むのが難しくなります –

答えて

4

あなたの二つの選択肢が同一です。マクロが定義されている順序は、マクロの展開とは無関係です。それは拡張時に定義されているものにのみ関係します。

どうしてですか?

マクロrepfを2つの引数で呼び出していますが、3つの引数をとります。これはエラーで、単純でシンプルなので、前処理は失敗します。

C++プリプロセッサの実装者にとって、この機能を回避できたと考えられる実装の問題は何ですか?

私はあなたがここで不当な仮定をしていると思います。問題は、プリプロセッサがいくつかの「機能」を「欠いている」ということではありません。プリプロセッサがどのように動作するかについてのあなたの期待は間違っています。

おそらく、あなたはプリプロセッサはこのような何かを期待する:ステップ1からステップ2へ

  1. repf(arr, stlf(arr))
  2. repf(arr, arr.begin(), arr.end())
  3. for(auto it = arr.begin() ; it != arr.end() ; ++it)

を...、stlf(arr)を取得拡張された;その展開はrepfへの呼び出しに入れられ、ステップ3で展開されます。

問題はプリプロセッサの動作ではありません。与えられた例では、私がこれを適切に手順を説明するので、我々は代わりに、例示の目的のためにこれを行うと仮定任せることはできない、壊れている:最後の行はx is A y is B c is Zに展開

#define FOO(X, Y) BAR(X, Y) 
#define BAR(X,Y,Z) x is X y is Y z is Z 
#define ACOMMAB A, B 
FOO(ACOMMAB, C) 

こと、そしてそれがより次のように動作します。

  1. FOO(ACOMMAB, C)
  2. BAR(ACOMMAB, C)
  3. BAR(A, B, C)
  4. x is A y is B c is Z

内部マクロは最初に展開されません。むしろ、外側のマクロがあります。この例はカンマを挿入することにも注意してください。コンマを注入することは間違いなくあなたが実際に行うことができます。私が避けていた「機能」であると仮定します。

どのように私はそのようなショートカットを使用できますか?

は、プリプロセッサでもスピードコーディングのために...あなたはそれが、あなたはおそらく、あなたが行うには、それを使用したいと思うものを行うためにそれを使用したくないんだと思うように動作しません考えます。 stlfは、関数呼び出しのために2つの引数を喜んでビルドしますが、マクロは関数ではありません。あなたの最善の策だと思われます。

+0

素敵な説明!!実装の順序がInner to Outerになっていたら、この機能は可能でしたか?また、内向きの拡張を検証する方法はありますか? –

+1

@premktiw「内向き拡張を検証する方法」ここに記載されているような順序で記述しないでください。この「順序」は、その動作を説明するための単なるデバイスです。ここで重要な点は、引数の拡張は呼び出しではなく置換リストに適用され、それは私が与えた例は元のものではないことが実証されていることです(g ++では'-E'フラグ、' -P'フラグはそのような出力をクリーンアップします)。この動作は、標準によって指定されています。 –

+0

... "オーダー"デバイスは、例外を視覚化するのにも役立ちます。たとえば、引数をさらに拡張するかどうかは、置換リストによって異なります。文字列化された引数または貼り付けられている引数は展開されません。実装はまた、置換リストに現れない引数を拡張することを禁じるかもしれない。しかし、厳密に言えば、引数を拡張することは置換リストに展開する前に発生する可能性があります。 –

0

それはこのようなものでなければなりません:

define repf(it, a) for(auto it = std::begin(a) ; it != std::end(a) ; ++it) 

しかし、なぜあなたはこのために、マクロを使用していますか?

+0

repf(it、a、b)を一般ループlike:int i; repf(i、0、10): - 私は10回繰り返します!! :) –

1

repfは3つの引数を必要とし、2つだけを渡しています。別のマクロの展開結果(stlf)を引数として使用することはできません(別のマクロの場合)。プリプロセッサ/コンパイラはこのように設計されていません。

私はマクロではない(多くの場合非常に便利です)が、この単純なケースではマクロを使用してはいけません。プログラムの読み込み、保守、デバッグが難しくなります。避ける!

理想的には、あなたの代わりに(何のマクロ)を使用すべきではないforループをもと範囲:

for(int a : arr) ... 
+0

私は議論として1つのマクロの拡張を使用することができます!私の主な質問は質問番号2です。競争力のあるプログラミングでも、これらのショートカットは多くの時間を節約します。例えば引数としてのマクロ展開の例を示します。sort(stlf(arr)); –

1

H・ウォルターズが指摘するように、問題は、マクロがない限り、入力トークンストリーム

  • からの引数が(これらの引数でマクロを展開するもの

    • 読み取り(解析)を行うれる順序です
    • これらの(展開された)引数をマクロ本体に代入してください。
    • 他のマクロを展開するために本文を再スキャンしてください。

    上記の2つの場所に他のマクロが展開されている可能性がありますので、順序が間違っている可能性がありますが、追加のマクロ呼び出しを追加することで正しく行うことができますあなたが望む時には物事を起こさせてください。

    expand(repf expand((arr, stlf(arr)))) 
    
    :あなたは正しい場所に追加のマクロ展開を得るためにそれを使用することができ、あなたは何もしないマクロ

    #define expand(...) __VA_ARGS__ 
    

    を持っている場合たとえば、(ただ1つ以上の引数を取り、それらにマクロを展開)

    は扱いにくいように見えるかもしれないが、あなたは別のrecrusiveマクロで展開して呼び出しをラップすることができ、あなたの希望for(auto it = arr.begin() ; it != arr.end() ; ++it)

    に展開されます:

    を3210

    、今repf(arr, stlf(arr))は、あなたがそれが望むように展開します。

  • 関連する問題