68

C / C ++で再帰マクロを使用できるかどうか知りたいですか?はいの場合、サンプル例を提供してください。

2番目のこと:なぜ私は以下のコードを実行できないのですか?私がしている間違いは何ですか?再帰的なマクロが原因ですか?

# define pr(n) ((n==1)? 1 : pr(n-1))
void main ()
{
    int a=5;
    cout<<"result: "<< pr(5) <<endl;
    getch();
}
4

5 に答える 5

140

マクロは再帰的に直接拡張されませんが、回避策があります。プリプロセッサがスキャンして展開するときpr(5)

pr(5)
^

無効化コンテキストを作成するため、pr再度表示すると次のようになります。

((5==1)? 1 : pr(5-1))
             ^

それは青く塗られ、何をしようとしても拡張できなくなります。ただし、遅延式といくつかの間接参照を使用することで、マクロが青く塗られるのを防ぐことができます。

# define EMPTY(...)
# define DEFER(...) __VA_ARGS__ EMPTY()
# define OBSTRUCT(...) __VA_ARGS__ DEFER(EMPTY)()
# define EXPAND(...) __VA_ARGS__

# define pr_id() pr
# define pr(n) ((n==1)? 1 : DEFER(pr_id)()(n-1))

これで、次のように拡張されます。

pr(5) // Expands to ((5==1)? 1 : pr_id ()(5 -1))

pr青く塗られたことは一度もないので、これは完璧です。さらに拡張するには、別のスキャンを適用する必要があります。

EXPAND(pr(5)) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : pr_id ()(5 -1 -1)))

2つのスキャンを適用して、さらに拡張することができます。

EXPAND(EXPAND(pr(5))) // Expands to ((5==1)? 1 : ((5 -1==1)? 1 : ((5 -1 -1==1)? 1 : pr_id ()(5 -1 -1 -1))))

ただし、終了条件がないため、十分なスキャンを適用することはできません。何を達成したいかはわかりませんが、再帰マクロを作成する方法に興味がある場合は、再帰繰り返しマクロを作成する方法の例を次に示します。

最初に、多くのスキャンを適用するマクロ:

#define EVAL(...)  EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL3(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define EVAL4(...) EVAL5(EVAL5(EVAL5(__VA_ARGS__)))
#define EVAL5(...) __VA_ARGS__

次に、パターンマッチングに役立つconcatマクロ:

#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)
#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__

インクリメントおよびデクリメントカウンター:

#define INC(x) PRIMITIVE_CAT(INC_, x)
#define INC_0 1
#define INC_1 2
#define INC_2 3
#define INC_3 4
#define INC_4 5
#define INC_5 6
#define INC_6 7
#define INC_7 8
#define INC_8 9
#define INC_9 9

#define DEC(x) PRIMITIVE_CAT(DEC_, x)
#define DEC_0 0
#define DEC_1 0
#define DEC_2 1
#define DEC_3 2
#define DEC_4 3
#define DEC_5 4
#define DEC_6 5
#define DEC_7 6
#define DEC_8 7
#define DEC_9 8

条件文に役立ついくつかのマクロ:

#define CHECK_N(x, n, ...) n
#define CHECK(...) CHECK_N(__VA_ARGS__, 0,)

#define NOT(x) CHECK(PRIMITIVE_CAT(NOT_, x))
#define NOT_0 ~, 1,

#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)
#define COMPL_0 1
#define COMPL_1 0

#define BOOL(x) COMPL(NOT(x))

#define IIF(c) PRIMITIVE_CAT(IIF_, c)
#define IIF_0(t, ...) __VA_ARGS__
#define IIF_1(t, ...) t

#define IF(c) IIF(BOOL(c))

#define EAT(...)
#define EXPAND(...) __VA_ARGS__
#define WHEN(c) IF(c)(EXPAND, EAT)

すべてをまとめると、繰り返しマクロを作成できます。

#define REPEAT(count, macro, ...) \
    WHEN(count) \
    ( \
        OBSTRUCT(REPEAT_INDIRECT) () \
        ( \
            DEC(count), macro, __VA_ARGS__ \
        ) \
        OBSTRUCT(macro) \
        ( \
            DEC(count), __VA_ARGS__ \
        ) \
    )
#define REPEAT_INDIRECT() REPEAT

//An example of using this macro
#define M(i, _) i
EVAL(REPEAT(8, M, ~)) // 0 1 2 3 4 5 6 7

したがって、いくつかの回避策を使用すると、C /C++で再帰マクロを使用できます。

于 2012-09-22T04:20:23.877 に答える
24

コンパイラには、実際にはコンパイルせず、前処理のみを行うオプションが用意されている可能性があります。これは、マクロで問題を見つけようとしている場合に役立ちます。たとえば、次を使用しg++ -Eます。

> g++ -E recursiveMacro.c

# 1 "recursiveMacro.c"
# 1 "<built-in>"
# 1 "<command line>"
# 1 "recursiveMacro.c"

void main ()
{
    int a=5;
    cout<<"result: "<< ((5==1)? 1 : pr(5 -1)) <<endl;
    getch();
}

ご覧のとおり、再帰的ではありません。pr(x)前処理中に一度だけ交換されます。覚えておくべき重要なことは、プリプロセッサが行うのは、あるテキスト文字列を別の文字列に盲目的に置き換えることだけであり、実際にはのような式を評価しないということ(x == 1)です。

コードがコンパイルさpr(5 -1)れない理由は、プリプロセッサによって置き換えられなかったため、未定義の関数の呼び出しとしてソースに格納されることになります。

于 2012-09-16T14:20:11.590 に答える
19

CまたはC++で再帰マクロを使用することは想定されていません。

C ++標準の関連言語、セクション16.3.4段落2:

置換リストのこのスキャン中に置換されるマクロの名前が見つかった場合(ソースファイルの残りの前処理トークンは含まれません)、置換されません。さらに、ネストされた置換で置換されるマクロの名前が検出された場合、そのマクロは置換されません。これらの置換されていないマクロ名前処理トークンは、そのマクロ名前処理トークンが置換されていたコンテキストで後で(再)検査された場合でも、それ以上の置換には使用できなくなります。

この言語には多少の揺れの余地があります。相互に呼び出す複数のマクロがある場合、その表現が何をすべきかを完全に示していない灰色の領域があります。この言語弁護士の問題に関しては、C++標準に対して活発な問題があります。http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#268を参照してください。

その言語弁護士の問題を無視すると、すべてのコンパイラベンダーはその意図を理解しています。

再帰マクロは、CまたはC++では許可されていません。

于 2012-09-16T14:34:26.310 に答える
11

コンパイルできないため、実行できない可能性があります。また、正しくコンパイルされた場合は、常に1が返されます(n==1)? 1 : n * pr(n-1)

マクロを再帰的にすることはできません。16.3.4.2章(Loki Astariに感謝)によると、現在のマクロが置換リストで見つかった場合、それはそのまま残されるためpr、定義内のマクロは変更されません。

置換リストのこのスキャン中に置換されるマクロの名前が見つかった場合(ソースファイルの残りの前処理トークンは含まれません)、置換されません。さらに、ネストされた置換で置換されるマクロの名前が検出された場合、そのマクロは置換されません。これらの置換されていないマクロ名前処理トークンは、そのマクロ名前処理トークンが置換されていたコンテキストで後で(再)検査された場合でも、それ以上の置換には使用できなくなります。

あなたの電話:

cout<<"result: "<< pr(5) <<endl;

プリプロセッサによって次のように変換されました。

cout<<"result: "<< (5==1)? 1 : pr(5-1) <<endl;

この間、prマクロの定義は「失われました」であり、。という名前の関数がないため、コンパイラは「このスコープ(ファクト)で「pr」が宣言されていません」などのエラーを表示しますpr

C ++では、マクロの使用は推奨されていません。関数を書いてみませんか?

この場合、コンパイル時に解決され、定数値として動作するようにテンプレート関数を作成することもできます。

template <int n>
int pr() {  pr<n-1>(); }

template <>
int pr<1>() { return 1; }
于 2012-09-16T14:27:00.163 に答える
-2

CまたはC++で再帰マクロを使用することはできません。

于 2012-09-16T14:18:06.103 に答える