C++中可变参数宏定义用法实践

本博文的目的是记录在C++中使用可变参数宏定义的过程,通过一些实际编写的代码和查看效果,来熟悉可变参数宏定义的使用方法和注意事项。
由于查询的资料来看,这种可变参数宏定义实际表现效果与所用的语言和编译器有关,因此首先列出我下面所有测试代码所用的语言和编译器版本,方便大家实际对照。
编译环境:
语言: C++ 11
编译器: gcc 5.4.0
系统: Ubuntu16.04

C++宏定义基本概念

在C++中使用宏定义想必大家都非常熟悉,宏定义可以方便定义一些常量、函数,也能进行编译条件的控制等作用等,例如:

#define PI 3.141592654
#define SUM(X, Y) (X) + (Y)

#ifdef DEBUG
#define DEBUG
#endif

但是不得不注意的是宏定义做的只是替换工作,直接将定义的宏变量或宏函数无条件替换成对应值,这样就会有很多注意事项,也在很多C++编程规范中建议尽量用其他方法来替代这种宏定义,例如使用const常量来替代宏定义常量。

不管宏定义有多少缺点和优点,这篇博文主要要讲的是利用宏定义来定义一些方便使用的可变参数宏函数

可变参数宏定义

1. 基本使用

可变参数宏定义(Macro With Variable Number of Arguments or Variadic Macro) 是指我们可以像定义能够接受不同数量参数的普通C++函数一样,定义一个能够接受不同数量参数的宏函数,这种宏定义的主要应用场合是可以实现消息打印函数的自定义,并可以控制其起作用的时机,例如:

#ifdef DEBUG
#define myprintf(...) printf(__VA_ARGS__)
#else myprintf(...)
#endif

上述代码自定义了一个字符串打印函数myprintf,其使用方法和printf函数相同,并且只会在定义了DEBUG宏之后才会起作用,这样就可以实现对我们所编写程序的打印控制——在调试阶段打印出各种调试信息,而在正式发布时,就取消打印。

2. 用法详解

2.1 基本用法

下面详细讲解一下可变参数宏定义基本语法。

#define myprintf(...) printf(__VA_ARGS__)

宏定义myprintf带有参数...,表示该宏定义可以接受多个参数,每个参数之间用逗号隔开(和普通函数参数格式一样)。在实际调用时,__VA_ARGS__会替代这些传入的所有参数(连同其中的逗号),从而作为printf函数的传入参数。即:

myprintf("CSDN zhanghm1995")
-> printf("CSDN zhanghm1995")
myprintf("CSDN zhanghm1995 is %d years", 2)
-> printf("CSDN zhanghm1995 is %d years", 2)

注:__VA_ARGS__替代的是宏函数中最后一个具名变量后的所有内容,包括逗号等所有符号,这里myprintf函数中并没有具名变量,因此替代的就会...所指代的所有内容。
注意只能用__VA_ARGS__,用其他的名字时会出现符号未定义的编译错误。

2.2 自定义格式用法

2.1中相当于是给printf函数重新命了一个函数名字,如果我们想要更加灵活的定制打印模式,比如每次打印之后默认进行换行,我们可以这样:

#define myprintf(format, ...) printf(format "\n", __VA_ARGS__)

这里myprintf函数定义了一个参数format用来对传入的第一个参数进行格式化,格式化的结果是在参数后面加了一个\n,即在传入的第一个参数后面都默认加了一个换行符。除了第一个参数,后面的所有参数都用__VA_ARGS__替代,传入到printf函数中。

看起来好像一切都很对,但编译时会出现错误:

error: expected primary-expression before ‘)’ token
 #define myprintf(format, ...) printf(format "\n", __VA_ARGS__)

原因是因为在2.1我们提到__VA_ARGS__是对宏函数中最后一个具名变量后的所有内容,包括逗号等所有符号进行替换,此处就是替换了, ...,因此实际传入到printf函数内容是:

printf(format "\n", , ...)

多了一个逗号。正确定义方式是:

#define myprintf(format, ...) printf(format "\n", ##__VA_ARGS__)

使用##符号来连接其中一个逗号。这样就编译正常了。

myprintf("CSDN zhanghm1995")
-> printf("CSDN zhanghm1995\n")
myprintf("CSDN zhanghm1995 is %d years", 2)
-> printf("CSDN zhanghm1995 is %d years\n", 2)

注:在自定义格式化参数时,format变量前后都需要加一个空格,否则可能会编译出错
例如:

#define myprintf(format, ...) printf("Hello "format "\n", ##__VA_ARGS__)

错误提示:

error: unable to find string literal operator ‘operator""format’ with ‘const char [7]’, ‘long unsigned int’ arguments
 #define myprintf(format, ...) printf("Hello"format "\n", ##__VA_ARGS__)

因此需要在format前添加一个空格

#define myprintf(format, ...) printf("Hello " format "\n", ##__VA_ARGS__)

【参考】:

  1. Variadic Macros
  2. 使用##__VA_ARGS__原因
  3. https://www.cnblogs.com/caosiyang/archive/2012/08/21/2648870.html
  • 11
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C++ 宏定义和 C 语言宏定义类似,也是一种预处理指令,用于在编译之前将源代码的标识符替换为指定的文本。不过,在 C++ 宏定义还可以用于实现一些类似函数的和模板元编程等高级特性。 C++ 宏定义的基本语法和 C 语言类似,如下所示: ```c++ #define 体 ``` 其名是标识符,体可以是任意合法的 C++ 表达式、语句或代码块。例如: ```c++ #define PI 3.1415926 #define MAX(a, b) ((a) > (b) ? (a) : (b)) ``` 上面的宏定义,第一个将名 PI 定义为常量 3.1415926,第二个将名 MAX 定义为求两个数的最大值的函数式。在源代码,可以通过名来引用体,例如: ```c++ float r = 1.0; float area = PI * r * r; int max_num = MAX(3, 5); ``` 与 C 语言相比,C++ 宏定义还有一些高级特性,包括: 1. 可变参数:类似于 C 语言可变参数函数,可以定义一个参数数量不定的,例如: ```c++ #define PRINT(fmt, ...) printf(fmt, ##__VA_ARGS__) ``` 上面的宏定义可以用来输出带格式的文本,类似于 printf 函数。 2. 预定义C++ 有一些预定义的,可以用来表示编译器、操作系统、编译时间等信息,例如: ```c++ #ifdef __cplusplus // C++ 代码 #else // C 代码 #endif ``` 上面的宏定义可以用来区分 C++ 代码和 C 代码。 3. 高级C++ 宏定义还可以用来实现一些高级特性,比如模板元编程、泛型编程等。例如: ```c++ #define STATIC_ASSERT(condition) \ typedef char static_assertion_##__LINE__[(condition) ? 1 : -1] ``` 上面的宏定义可以用来实现静态断言,类似于 C++11 的 static_assert。 需要注意的是,宏定义的滥用会导致代码的可读性和可维护性变差,因此需要谨慎使用宏定义,尽量使用 C++11 的 constexpr、inline、template 等语言特性替代宏定义。此外,还需要注意宏定义的展开顺序和优先级,以避免出现错误或者副作用。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值