C++20中的预处理器宏——__VA_OPT__

一、预处理器宏

预处理,Preprocessing,它是在正式编译前对源代码的一种文本层面的处理。主要包括宏展开、文件包含、条件编译以及注释等的处理。预处理器则是执行上面的预处理的一种工具,它会不涉及到语法或语义的操作。预处理器宏就是宏,与普通的宏没有本质区别。不过,它是用于进行预处理的。常见的预处理器宏有:#ifdef、#ifndef、#if等以及一些特定的功能宏如_FILE_ 和 __LINE__等。

二、_VA_OPT _ 和 VA_ARGS

在C语言的printf函数的实现中,大家都接触过__VA_ARGS__这个宏。这个宏在应用时有不少的小细节需要处理。其中,##_VA_ARGS__即VA_ARGS__前面加上"##"可以处理当可变参数个数为0的时候,去掉前面多余的逗号(","),防止编译器报错。不过这个是GNU编译器的扩展,不属于标准库中的定义。可以参看看下面的示例代码。
而在C++20中为标准化统一,提供了"_VA_OPT
"这个宏用来处理空参数的问题。

c 复制代码
#define PRINT(...) printf(__VA_ARGS__)
#define PRINT_FMT(format, ...) printf(format, __VA_ARGS__)
#define PRINT_GNU(format, ...) printf(format, ##__VA_ARGS__)
#define PRINT_CPP20(format, ...) printf(format __VA_OPT__(, ) __VA_ARGS__)
int main() {
  PRINT("test macro print\n");
  // PRINT_FMT("test macro print fmt\n");//空参数
  PRINT_FMT("test macro print fmt %d\n", 100);
  PRINT_GNU("test macro print gnu\n");
  PRINT_GNU("test %s", "my print\n");
  return 0;
}

三、分析说明

在上面也看到了__VA_ARGS__和__VA_OPT__的应用,__VA_OPT__应用的方法是:

c 复制代码
__VA_OPT__(content)

对其来说当 VA_ARGS__展开非零参数时,插入"展开的参数";当*VA_ARGS* 展开为空(0参数)时,忽略","或其它引起错误的情况。虽然__VA_OPT__是C++20标准提出的,但要使用还是需要看编译器的具体支持。其优势在于处理老式的可变参数宏处理时的漏洞,从而有条件的处理不同情况下的宏展开。特别是在与兼容C语言开发时,有着重要的作用。

不过,对于C++来说,如果不想使用__VA_OPT__可以使用变参模板和参数包展开等方式来来替代,这就看实际应用的具体情况了。

四、应用

宏的应用还是比较多的,只是不被推荐罢了。__VA_OPT__可以应用于条件处理、复杂数据的创建以及变参模板的支持等。看下面例程:

c 复制代码
#include <iostream>
#include <tuple>
// tuple create
#define CREATE_TUPLE(...) std::make_tuple(__VA_OPT__(__VA_ARGS__))

//   create a named tuple
#define NAME_TUPLE(name, ...) std::tuple_cat(std::make_tuple(name) __VA_OPT__(, std::make_tuple(__VA_ARGS__)))

void test() {
  auto a = CREATE_TUPLE();
  auto b = CREATE_TUPLE(1, 'a', 2.0, "abc");

  auto c = NAME_TUPLE("single");
  auto d = NAME_TUPLE("two", 1, 1.1);
  auto e = NAME_TUPLE();

  std::cout << std::tuple_size<decltype(a)>::value << std::endl;
  std::cout << std::tuple_size<decltype(b)>::value << std::endl;
  std::cout << std::tuple_size<decltype(c)>::value << std::endl;
  std::cout << std::tuple_size<decltype(d)>::value << std::endl;
  std::cout << std::tuple_size<decltype(e)>::value << std::endl;
  std::cout << std::get<1>(d) << std::endl;
  std::cout << std::get<0>(c) << std::endl;
}
int main() {
  test();
  return 0;
}

五、总结

虽然说宏的应用场景在不断的被压缩,但在某些场景下,还是无法替代的。特别是在预处理时,宏的应用非常广泛。所以C++20提供__VA_OPT__用于解决__VA_ARGS__的空参数的特殊场景也是可以理解的。这也可以明白为什么C/C++标准迭代的复杂性,历史既是功绩也是包袱。

相关推荐
啦啦啦啦啦zzzz8 天前
利用RAII机制进行日志的采集
linux·服务器·c++·网络编程·c++20
啦啦啦啦啦zzzz9 天前
Logger的组装(c++20)
c++·算法·c++20
啦啦啦啦啦zzzz11 天前
Appender抽象类的实现c++20
linux·服务器·c++·c++20
OPEN-F14 天前
C++20新特性精讲:概念、范围与三路比较
算法·c++20
小小龙学IT22 天前
C++ std::chrono 时间库深度解析:从 duration 模板到 C++20 日历与时区
linux·c++·c++20
郝学胜-神的一滴24 天前
C++20 高级编程 004:从初始化、内存到const系列关键字
开发语言·算法·编程·软件构建·c++20
可爱系程序猿25 天前
msvcp120.dll 加载失败排查:VC++ 2013、进程架构与插件私有目录如何对应
程序人生·电脑·c++20
ShineWinsu25 天前
对于C++:C++20中线程、初始化、Lambda与内存视图等特性的解析
c++·c++20
可爱系程序猿25 天前
mfc100.dll 加载失败排查:旧版 VC++ 2010 程序、位数与插件目录如何核对
程序人生·电脑·c++20
ShineWinsu1 个月前
对于 C++:C++20中Concept(概念) 与 Coroutine(协程)的解析
linux·开发语言·网络·c++·c++20·epoll