VisualStudio 的零参数参数计数宏

4

gcc支持使用## __VA_ARGS__约定的零参数宏。以下代码在gcc编译器下可以正常工作:

#include <stdio.h>

#define NARGS(...) __NARGS(0, ## __VA_ARGS__, 5,4,3,2,1,0)
#define __NARGS(_0,_1,_2,_3,_4,_5,N,...) N

int main()
{
  printf("%d\n", NARGS());     // prints 0
  printf("%d\n", NARGS(1));    // prints 1
  printf("%d\n", NARGS(1, 2)); // prints 2
  return 0;
}

有没有与VisualC++等效的工具,可以使用零参数宏?接受非标准扩展或技巧。

编辑:修复了示例,使其能够使用GCC扩展和C++编译器。


1
请注意,g++(与gcc不同)还会打印1:(https://ideone.com/DTIkRF)。 - Jarod42
@Jarod42,有点奇怪。那么在C++中如何计算宏参数呢?我已经找到了一个适用于VisualC++的正确解决方案。 - ceztko
是的,我昨天也发现了。目前还没有人对此进行适当的反驳。我稍后会尝试理解这个宏。 - ceztko
@Jarod32:现在这个例子也可以在GCC上运行了。加上我为MSVC找到的解决方案,这个问题对我来说已经解决了。 - ceztko
相关问题:宏计算参数数量 - Eitan T
显示剩余3条评论
4个回答

15

下面的示例在VisualStudio 2010及以上版本、启用了非标准扩展的gcc和clang中都可以正常工作。在Microsoft编译器中,它假定AUGMENTER宏中的尾逗号将在参数计数为零时被预处理器移除。这是非标准的,也已经在其他地方报告过。在gcc和clang中,它使用广泛知晓的## __VA_ARGS__非标准扩展。

#include <stdio.h>

#ifdef _MSC_VER // Microsoft compilers

#define EXPAND(x) x
#define __NARGS(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARGS_1(...) EXPAND(__NARGS(__VA_ARGS__, 4, 3, 2, 1, 0))

#define AUGMENTER(...) unused, __VA_ARGS__
#define NARGS(...) NARGS_1(AUGMENTER(__VA_ARGS__))

#else // Others

#define NARGS(...) __NARGS(0, ## __VA_ARGS__, 5,4,3,2,1,0)
#define __NARGS(_0,_1,_2,_3,_4,_5,N,...) N

#endif

int main()
{
  // NARGS
  printf("%d\n", NARGS());          // Prints 0
  printf("%d\n", NARGS(1));         // Prints 1
  printf("%d\n", NARGS(1, 2));      // Prints 2
  fflush(stdout);

#ifdef _MSC_VER
  // NARGS minus 1
  printf("\n");
  printf("%d\n", NARGS_1(1));       // Prints 0
  printf("%d\n", NARGS_1(1, 2));    // Prints 1
  printf("%d\n", NARGS_1(1, 2, 3)); // Prints 2
#endif

  return 0;
}

宏已经在真实的编译器中进行了测试,其中包括 WandboxWebcompiler


不错:有人给我负评却没有写原因。这些宏花费了数小时的工作时间,所以我很想知道它们是否对某些人无效。 - ceztko
哇!EXPAND(x) x 看来是我在 Visual Studio 中让我的参数计数器宏工作所缺失的东西。我不明白为什么这对非 GNU gcc/clang 不起作用。无论如何,我会为这个解决方案点赞,因为它为我节省了数小时的研究时间。谢谢! - mchiasson
EXPAND(x) x 的需求实际上是 VS2010 预处理器中的一个错误,在后续版本中已经修复。 - ceztko
@BLUEPIXY:你有注意到“启用非标准扩展”吗?也许这也解释了为什么会被踩。 - Technophile
据我所知,没有干净的方法可以在没有非标准扩展的情况下实现这一点。我发现的其他解决方案都是非常肮脏的技巧,只会使预处理器紧张并在某些有用的用例中失败。曾经提出过将 ## __VA_ARGS__ 添加到标准中的建议,但由于某些原因而失败。在标准中解决此问题之前,所有现有的解决方案都存在某种缺陷。 - ceztko
显示剩余2条评论

2

这里有一些使用gcc(5.3) VisualStudio2010可用的代码:

#include <stdio.h>

#define expand(x)                          x
#define prefix(...)                        0,##__VA_ARGS__
#define lastof10(a,b,c,d,e,f,g,h,i,j,...)  j
#define sub_nbarg(...)                     expand(lastof10(__VA_ARGS__,8,7,6,5,4,3,2,1,0))
#define nbarg(...)                         sub_nbarg(prefix(__VA_ARGS__))

#define test(...) printf("(%s) ---> %d\n",""#__VA_ARGS__,nbarg(__VA_ARGS__))

int main () 
    {
    test() ;              // () ---> 0
    test(1) ;             // (1) ---> 1
    test(1,2) ;           // (1,2) ---> 2
    test(1,2,3) ;         // (1,2,3) ---> 3
    test(1,2,3,4) ;       // (1,2,3,4) ---> 4
    test(1,2,3,4,5) ;     // (1,2,3,4,5) ---> 5
    test(1,2,3,4,5,6) ;   // (1,2,3,4,5,6) ---> 6
    return 0 ;
    }

我很久以前就用非标准扩展解决了这个问题,但我很好奇并测试了你的方法。我从lastof10中删除了括号,以便在预处理器之后扩展恰好是0、1、...的参数数量,而不是(0)、(1)、...。同样的例子在VS2010和gcc中都可以工作。在您的用例中是否有效?我应该使用我的复杂宏系统测试您的方法,以确认您的解决方案适用于我的用例(我使用参数数量创建新的宏),但感谢您的调查和提出解决方案。 - ceztko
对我来说真的不是问题,但是你的方法使用了非标准扩展:它可以在gcc中使用-std=gnu++11-std=gnu99,但不能-std=c++11-std=c99中使用。你的方法的优点是它在主要编译器(*nix和Windows世界)上都是可移植的。 - ceztko

0

你能试试这个吗:

#define __NARGS(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARGS(...) (sizeof(#__VA_ARGS__) == sizeof("") ? 0 : __NARGS(__VA_ARGS__, 5, 4, 3, 2, 1))

这可以使用g++编译器运行(演示)。


这不适用于VC++2010。在VC中,sizeof(#__VA_ARGS__)会扩展为sizeof(),而sizeof()是语法错误。 - BLUEPIXY
同时它并没有解决基本的MSVC问题。 - BLUEPIXY
这种方法很局限,因为它要求代码被有效编译。 - ceztko

-1

使用@Jarod42的思路和BOOST_PP_VARIADIC_SIZE,可以编写如下代码。

#include <stdio.h>
#include <boost/preprocessor/variadic/size.hpp>

#define FOO(...) (sizeof(""#__VA_ARGS__) == sizeof("") ? 0 : BOOST_PP_VARIADIC_SIZE(__VA_ARGS__))

int main()
{
  printf("%d\n", FOO());     // prints 0
  printf("%d\n", FOO(1));    // prints 1
  printf("%d\n", FOO(1, 2)); // prints 2
  return 0;
}

1
这是相当无用的,因为替换项不是整数字面量。 - Columbo
@Loopunroller 这是什么意思?请指出不好的具体例子。 - BLUEPIXY
这种方法也有局限性,需要编译代码并依赖于boost。你可以尝试一下我回答中的那个方法,看看它是否也适用于你? - ceztko
@ceztko 你可以参考boost的实现。 - BLUEPIXY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接