Maison  >  Article  >  développement back-end  >  Pourquoi l'expansion des macros variadiques diffère-t-elle entre MSVC et GCC ?

Pourquoi l'expansion des macros variadiques diffère-t-elle entre MSVC et GCC ?

Patricia Arquette
Patricia Arquetteoriginal
2024-11-05 08:36:02934parcourir

Why Does Variadic Macro Expansion Differ Between MSVC   and GCC?

Extension de macro variadique dans MSVC

Le mécanisme de macro variadique dans MSVC se comporte différemment de celui de GCC, ce qui entraîne des difficultés pour développer des macros avec plusieurs arguments. Considérez les macros suivantes :

<code class="cpp">#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)</code>

Dans GCC, cette macro s'étendrait à :

<code class="cpp">struct MyStructure
{
  void Foo()
  {
    parent->GetProperty<Property1>(); 
    parent->GetProperty<Property2>(); 
    parent->GetProperty<Property3>(); 
    parent->GetProperty<Property4>();
  }

  Base * parent;
}</code>

Cependant, dans MSVC, elle s'étendrait à :

<code class="cpp">struct MyStructure
{
  void Foo()
  {
    parent->GetProperty<Property1, Property2, Property3, Property4>();
  }

  Base * parent;
}</code>

Explication

Le problème se pose car MSVC applique l'expansion des macros niveau par niveau, tandis que GCC étend complètement les macros en un seul passage. Considérez l'invocation de la macro :

<code class="cpp">EXPAND_THESE(Property1, Property2, Property3, Property4)</code>
  • MSVC :

    1. VA_NARGS(Property1, Property2, Property3, Property4) -> 4
    2. SEMI_EXPANDED(4, Propriété1, Propriété2, Propriété3, Propriété4) -> MAC4(Propriété1, Propriété2, Propriété3, Propriété4)
    3. EXPAND_THESE(MAC4(Propriété1, Propriété2, Propriété3, Propriété4)) -> MAC4(Propriété1, Propriété2, Propriété3, Propriété4)
  • GCC :

    1. VA_NARGS(Propriété1, Propriété2, Propriété3, Propriété4) -> 4
    2. SEMI_EXPANDED(4, Propriété1, Propriété2, Propriété3, Propriété4) -> MAC4(Propriété1, Propriété2, Propriété3, Propriété4)
    3. EXPAND_THESE(MAC4(Propriété1, Propriété2, Propriété3, Propriété4)) -> MAC4 (Propriété1, Propriété2, Propriété3, Propriété4)
      ---> Déplie davantage la macro MAC4
      ---> Équivalent à plusieurs lignes d'appels ACTUAL_MACRO

Solution de contournement

Pour obtenir un comportement similaire à GCC, on peut utiliser l'approche de Jeff Walden, qui implique de créer des assistants de macro supplémentaires et d'utiliser la macro COUNT_ARGS_MAX5 pour déterminer le nombre d'arguments :

<code class="cpp">#define FOO_CHOOSE_HELPER1(count) FOO##count
#define FOO_CHOOSE_HELPER2(count) FOO_CHOOSE_HELPER1(count)
#define FOO_CHOOSE_HELPER(count) FOO_CHOOSE_HELPER2(count)

#define ERROR(...) GLUE(FOO_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__))

#define ASSERT1(expr) singleArgumentExpansion(expr)
#define ASSERT2(expr, explain) twoArgumentExpansion(expr, explain)
#define ASSERT(...) GLUE(FOO_CHOOSE_HELPER(COUNT_ARGS_MAX5(__VA_ARGS__)), (__VA_ARGS__))</code>

Alternativement, l'abstraction fournie par la macro "GLUE" peut être utilisée pour simplifier la solution de contournement :

<code class="cpp">#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__);</code>

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn