4

マクロを使用して関数宣言を生成しようとしています

/* goal: generate int f(int a, float b) */
template<typename P>
struct ptype;

template<typename P>
struct ptype<void(P)> { typedef P type; };

#define NAMEe
#define COMMAe
#define COMMA ,

#define NAME(N) N PARAMS
#define PARAMS(P, ...) COMMA ## __VA_ARGS__ P NAME ## __VA_ARGS__
#define PARAM_ITER(P) P NAME

#define PROTO(R, N, P)  \
  ptype<void R>::type N (PARAM_ITER P (,e))

PROTO((int), f, (int)(a)(float)(b));

空のマクロ引数を使用して、next(name)または(type)byNAMEまたはPARAMSそれぞれを繰り返し処理します。...しかし、GCCは

prototype.hpp:20:35: warning: ISO C99 requires rest arguments to be used

そしてclangは不平を言う

ptype<void (int)>::type f (int aprototype.hpp:20:1: warning: varargs argument missing, but tolerated as an extension [-pedantic]

これは次の理由で起こると思います

#define FOO(X, ...)
FOO(A);

私は、...または それぞれの または(name)の引数を渡していないからです(type)。適用できる簡単な回避策はありますか?


@James がパラメーター リストの長さを見つけるために使用する手法と同様の手法を使用しました。2 番目の引数として , の代わりに が渡された場合OONTカンマと. を出力しますNAME。最終的な解決策は次のとおりです。

/* goal: generate void f(int a, float b) */
template<typename P>
struct ptype;

template<typename P>
struct ptype<void(P)> { typedef P type; };

#define TYPE_DO(X) X
#define TYPE_DONT(X)
#define TYPE_MAYBE(X, A, ...) TYPE_D ## A (X)

#define COMMA_DO ,
#define COMMA_DONT
#define COMMA_MAYBE(A, B, ...) COMMA_D ## B

#define NAME_DO NAME
#define NAME_DONT
#define NAME_MAYBE(A, B, ...) NAME_D ## B

#define NAME(N) N PARAMS
#define PARAMS(...) COMMA_MAYBE(__VA_ARGS__,O,O) TYPE_MAYBE(__VA_ARGS__,O,O) \
                    NAME_MAYBE(__VA_ARGS__,O,O)
#define PARAM_ITER(P) P NAME

#define PROTO(R, N, P)  \
  ptype<void R>::type N (PARAM_ITER P (D,ONT))

テスト:

#define STR1(X) #X
#define STR(X) STR1(X)

int main() {
  // prints correctly
  std::cout << STR(PROTO((int), f, (int)(a)(float)(b)));
}
4

2 に答える 2

6

" " 問題を解決するためFOOに、可変引数パックのアリティに応じて異なるマクロを選択できます。これが最初のショットです。

// These need to be updated to handle more than three arguments:
#define PP_HAS_ARGS_IMPL2(_1, _2, _3, N, ...) N
#define PP_HAS_ARGS_SOURCE() MULTI, MULTI, ONE, ERROR

#define PP_HAS_ARGS_IMPL(...) PP_HAS_ARGS_IMPL2(__VA_ARGS__)
#define PP_HAS_ARGS(...)      PP_HAS_ARGS_IMPL(__VA_ARGS__, PP_HAS_ARGS_SOURCE())

#define FOO_ONE(x)     ONE_ARG:    x
#define FOO_MULTI(...) MULTI_ARG:  __VA_ARGS__

#define FOO_DISAMBIGUATE2(has_args, ...) FOO_ ## has_args (__VA_ARGS__)
#define FOO_DISAMBIGUATE(has_args, ...) FOO_DISAMBIGUATE2(has_args, __VA_ARGS__)
#define FOO(...) FOO_DISAMBIGUATE(PP_HAS_ARGS(__VA_ARGS__), __VA_ARGS__)

使用例:

FOO(1)     // replaced by ONE_ARG:   1
FOO(1, 2)  // replaced by MULTI_ARG: 1, 2

(これを再検討してクリーンアップしようとします。間違いなく不要なマクロがいくつかあると思います。あなたが説明したより広い問題を調べる機会がなかったので、これで解決するかどうかわかりませんその問題を解決するもっと簡単な方法もあるかもしれません...私は特に可変引数マクロに精通していません.これはmcppできれいに前処理されます.)

于 2011-03-18T17:53:01.337 に答える
2

P99には、あなたが望むことを正確に実行するマクロがありますP99_PROTOTYPE。それはの「署名」を持っています

P99_PROTOTYPE(RT, NAME [, AT]*)

ここRTで、は戻り型(である可能性がありますvoid)でありAT、は引数型です。引数タイプのリストは空の場合があり、その場合は。に置き換えられvoidます。

P99はC++用ではなくC99用に作成されていることに注意してください。引数にコンマが含まれていると、特に問題が発生します。C ++の構文によるトークンの乱用、<および>テンプレートのブラケット式としての悪用は、プリプロセッサにとって特に悪いことです。CプリプロセッサとC++は、基本的に構文レベルで互換性のない言語です。

P99は、マクロが呼び出しで受け取る引数の数を検出し、境界の場合で異なる反応をすることで、直面している問題を回避します。

于 2011-03-19T17:17:14.483 に答える