25

そのため、GCCではうまく機能するマクロがありますが、MicrosoftのC++コンパイラでは機能しません。誰かが回避策を知っているか、おそらくそれがこのように動作する理由を私に説明できることを願っています。

このマクロは正確に「標準」ではないと確信していますが、それは本当に役に立ちます。

マクロの機能例を次に示します。

#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 5, 4, 3, 2, 1)

#define FULLY_EXPANDED(count, ...) \
  MAC ## count (__VA_ARGS__)

#define SEMI_EXPANDED(count, ...) FULLY_EXPANDED(count, __VA_ARGS__)

#define EXPAND_THESE(...) SEMI_EXPANDED(VA_NARGS(__VA_ARGS__), __VA_ARGS__)

#define ACTUAL_MACRO(x) parent->GetProperty<x>();
#define MAC1(a) ACTUAL_MACRO(a)
#define MAC2(a,b) MAC1(a) ACTUAL_MACRO(b)
#define MAC3(a,b,c) MAC2(a,b) ACTUAL_MACRO(c)
#define MAC4(a,b,c,d) MAC3(a,b,c) ACTUAL_MACRO(d)
#define MAC5(a,b,c,d,e) MAC4(a,b,c,d) ACTUAL_MACRO(e)

このマクロの使用方法は次のとおりです。

struct MyStructure
{
  void Foo()
  {
    EXPAND_THESE(Property1, Property2, Property3, Property4)
  }

  Base * parent;
}

GCCが上記を拡張する方法は次のとおりです。

struct MyStructure
{
  void Foo()
  {
    parent->GetProperty<Property1>(); 
    parent->GetProperty<Property2>(); 
    parent->GetProperty<Property3>(); 
    parent->GetProperty<Property4>();
  }

  Base * parent;
}

しかし、Microsoftは何らかの理由で、私のすべての__VA_ARGS__を1つの引数として拡張しています。

struct MyStructure
{
  void Foo()
  {
    parent->GetProperty<Property1, Property2, Property3, Property4>();
  }

  Base * parent;
}

なぜこれなのか誰か知っていますか?MicrosoftにGCCのようにこれを拡張させるために私が引っ張ることができるいくつかのトリックはありますか?たぶん、括弧のいくつかの余分なペアを投げますか?

このようなマクロは、大量の「グルー」コードを置き換えるのに非常に役立ちますが、この問題のため、VSプロジェクトに移動できません。どんな助けでも大歓迎です!

ありがとう。

4

3 に答える 3

23

この質問は2年以上前のものですが、私と同じように、まだこれに遭遇している人にはもっと洗練された答えを出そうと思いました。

Jeff Waldenの答えはすべて機能しますが、可変個引数を設定するFOOマクロごとにFOO_CHOOSE_HELPER/1/2を宣言する必要があります。私はこの問題を解決するために抽象化レイヤーを開発しました。次のことを考慮してください。

#define GLUE(x, y) x y

#define RETURN_ARG_COUNT(_1_, _2_, _3_, _4_, _5_, count, ...) count
#define EXPAND_ARGS(args) RETURN_ARG_COUNT args
#define COUNT_ARGS_MAX5(...) EXPAND_ARGS((__VA_ARGS__, 5, 4, 3, 2, 1, 0))

#define OVERLOAD_MACRO2(name, count) name##count
#define OVERLOAD_MACRO1(name, count) OVERLOAD_MACRO2(name, count)
#define OVERLOAD_MACRO(name, count) OVERLOAD_MACRO1(name, count)

#define CALL_OVERLOAD(name, ...) GLUE(OVERLOAD_MACRO(name, COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__))

このアーキテクチャを使用すると、可変個引数マクロを次のように定義できます。

#define ERROR1(title) printf("Error: %s\n", title)
#define ERROR2(title, message)\
    ERROR1(title);\
    printf("Message: %s\n", message)
#define ERROR(...) CALL_OVERLOAD(ERROR, __VA_ARGS__)

#define ASSERT1(expr) singleArgumentExpansion(expr)
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain)
#define ASSERT(...) CALL_OVERLOAD(ASSERT, __VA_ARGS__)

ジェフの答えでは、マクロを次のように定義する必要があります。

#define ERROR1(title) printf("Error: %s\n", title)
#define ERROR2(title, message)\
    ERROR1(title);\
    printf("Message: %s\n", message)

#define ERROR_CHOOSE_HELPER2(count) ERROR##count
#define ERROR_CHOOSE_HELPER1(count) ERROR_CHOOSE_HELPER2(count)
#define ERROR_CHOOSE_HELPER(count) ERROR_CHOOSE_HELPER1(count)

#define ERROR(...) GLUE(ERROR_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\
    (__VA_ARGS__))

#define ASSERT1(expr) singleArgumentExpansion(expr)
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain)

#define ASSERT_CHOOSE_HELPER2(count) ASSERT##count
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count)
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count)

#define ASSERT(...) GLUE(ASSERT_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)),\
    (__VA_ARGS__))

大したことではありませんが、コードをできるだけ簡潔にするのが好きです。また、複数の可変個引数マクロを使用している場合は、コードの重複や発生する可能性のある複雑さを減らすのに指数関数的に役立ちます。私の知る限り、この方法は移植性もあります。私はそれを最も一般的なコンパイラの多くでテストしましたが、同じ結果が得られました。

使用例:

int foo()
{
    ASSERT(one); // singleArgumentExpansion(one)
    ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy")

    ERROR("Only print a title");
    ERROR("Error title", "Extended error description");
}
于 2014-06-04T02:55:55.920 に答える
18

偶然にも、私はちょうど今日この問題に遭遇しました、そして十分な努力の後に私は私自身の目的のための解決策を見つけたと思います。バグは、MSVCが__VA_ARGS__引数リストで単一のトークンとして扱うことです。ただし、マクロ呼び出し引数リスト内で直接使用しないことで、これを回避できます。 このコメントは、あなたの問題に対する答えの始まりを示唆しています。

#define VA_NARGS(...) VA_NUM_ARGS_IMPL_((__VA_ARGS__, 5,4,3,2,1))
#define VA_NARGS_IMPL_(tuple) VA_NUM_ARGS_IMPL tuple
#define VA_NARGS_IMPL(_1,_2,_3,_4,_5,N,...) N

しかし、そうすると、それが実際の「N」に完全に拡張されることを確認するという問題に遭遇する可能性が高いと思いますVA_NARGS_IMPL (arg1, arg2, 5, 4, 3, 2, 1)。私のコード(あなたのように見えた)は、MAC##codeすべてを1つのユニットとして拡張するために変更する必要があり、それを引数リストと個別に組み合わせる必要があることがわかりました。これが私が私のために働いたと思ったコードです:

#define ASSERT_HELPER1(expr) singleArgumentExpansion(expr)
#define ASSERT_HELPER2(expr, explain) \
   twoArgumentExpansion(expr, explain)

/*
 * Count the number of arguments passed to ASSERT, very carefully
 * tiptoeing around an MSVC bug where it improperly expands __VA_ARGS__ as a
 * single token in argument lists.  See these URLs for details:
 *
 *   http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement
 *   http://cplusplus.co.il/2010/07/17/variadic-macro-to-count-number-of-arguments/#comment-644
 */
#define COUNT_ASSERT_ARGS_IMPL2(_1, _2, count, ...) \
   count
#define COUNT_ASSERT_ARGS_IMPL(args) \
   COUNT_ASSERT_ARGS_IMPL2 args
#define COUNT_ASSERT_ARGS(...) \
   COUNT_ASSERT_ARGS_IMPL((__VA_ARGS__, 2, 1, 0))
 /* Pick the right helper macro to invoke. */
#define ASSERT_CHOOSE_HELPER2(count) ASSERT_HELPER##count
#define ASSERT_CHOOSE_HELPER1(count) ASSERT_CHOOSE_HELPER2(count)
#define ASSERT_CHOOSE_HELPER(count) ASSERT_CHOOSE_HELPER1(count)
 /* The actual macro. */
#define ASSERT_GLUE(x, y) x y
#define ASSERT(...) \
   ASSERT_GLUE(ASSERT_CHOOSE_HELPER(COUNT_ASSERT_ARGS(__VA_ARGS__)), \
               (__VA_ARGS__))

int foo()
{
  ASSERT(one); // singleArgumentExpansion(one)
  ASSERT(two, "foopy"); // twoArgumentExpansion(two, "foopy")
}

数時間後に自分の問題を解決して、あなたの問題を完全に解決するには、私の心はあまりにもどろどろです、申し訳ありません。:-)しかし、少しの作業で、これでうまくいくものにたどり着くには十分だと思います。

于 2012-02-18T04:06:05.287 に答える
7

MicrosoftはC/C ++プリプロセッサを書き直しましたが、デフォルトでは「下位互換性」が有効になっていません。つまり、移植性や標準準拠ではなく、自社製品とのバグ互換性を優先しています。

コマンドラインにフラグを追加することで__VA_ARGS__処理を修正できるようです。/experimental:preprocessor

于 2020-07-13T19:20:03.817 に答える