为什么在扩展[fancy]宏时avr-gcc会告诉我有关缺少参数的信息?

用户名

我正在为Atmel开发代码,该代码定义了与程序端口引脚一致的寄存器名称。例如:

  • 传统上,PORTC用于设置(高或低)端口C上的任何引脚
  • PINC用于读取端口C上特定引脚的状态
  • DDRC用于读取/写入端口C上任何引脚的方向(0 =输入,1 =输出)

因此,我找到了一些我完全理解的代码,它们定义了PIN宏,如下所示:

#define LED_PIN    C,7

然后是以下宏(仅限于我在这里谈论的用例):

#define _BV(bit)                        (1 << bit)
...
#define _SET(type, name, bit)           type ## name |= _BV(bit)
#define _CLEAR(type, name, bit)         type ## name &= ~ _BV(bit)
#define _TEST(type, name, bit)          ( type ## name  & _BV(bit) )
...
#define OUTPUT(pin)         _SET(DDR, pin)
#define INPUT(pin)          _CLEAR(DDR, pin)
...
#define HIGH(pin)           _SET(PORT, pin)
#define LOW(pin)            _CLEAR(PORT, pin)

因此,我可以编写main()如下函数

main() {
  OUTPUT(LED_PIN);
  HIGH(LED_PIN);
}

尽管它非常方便,并且可以防止仅为一个LED定义三个宏(例如PINC7PORTC7DDRC7),但我对此的主要担心是,考虑到AVR,还必须重新定义AVR宏以考虑此类标记宏使用SFR寄存器作为参数(来自sfr_defs.h):

#define bit_is_set(sfr, bit) (_SFR_BYTE(sfr) & _BV(bit))

此外,AVR定义了宏以将[我称之为]原始注册表名称转换为实际的内存映射特殊功能寄存器名称(sfr)avr-gcc

#define _SFR_BYTE(sfr) _MMIO_BYTE(_SFR_ADDR(sfr))

_SFR_ADDR()根据目标Atmel CPU添加偏移量0或20。因此,为了增强与那些带有SFR寄存器和可选参数的AVR库函数的兼容性,我决定重写初始代码并尝试以下操作:

#define _SFR_BIT(type, name, bit)   type ## name, bit
#define _PORT(pin)                  _SFR_BIT(PORT, pin)
#define _PIN(pin)                   _SFR_BIT(PIN, pin)
#define _DDR(pin)                   _SFR_BIT(DDR, pin)

#define set_bit(sfr, bit)           _SFR_BYTE(sfr) |= _BV(bit)
#define set_output(pin)             set_bit(_PIN(pin))

但是,一旦我在函数中编写如下内容,我就会遇到编译器错误消息main()

set_output(LED_PIN);

main.cpp:72:19:错误:宏“ set_bit”需要2个参数,但仅给定1个

如果我也尝试此操作,也会遇到相同的错误:

#define set_output(pin)             set_bit(_SFR_BIT(DDR, pin))

为什么该宏OUTPUT()只传递三个参数中的两个_SET()编译,而我的宏却不能呢?


正如Jens Gustedt所指出的那样,一般性解释在于编译器解析宏的顺序。

若要使用宏将任意数量的参数传递给具有固定数量的参数的宏,可以将函数名称设为宏的参数:

#define EXPAND(f, ...)              f(__VA_ARGS__)
#define _SFR_BIT(type, name, bit)   type ## name, bit
...
#define set_bit(sfr, bit)           _SFR_BYTE(sfr) |= _BV(bit)
...
#define set_output(pin)             EXPAND(set_bit, _SFR_BIT(PIN, pin))

在没有任何“参数丢失”编译器错误的情况下,解析了传递给函数的参数数量(第一个)

精简版:

#define _SFR_X(f, type, name, bit)  f(type ## name, bit)
...
#define set_bit(sfr, bit)           _SFR_BYTE(sfr) |= _BV(bit)
...
#define set_output(pin)             _SFR_X(set_bit, PIN, pin)

免责声明

该代码尚未投入生产。当然,代码可能很难看。这主要是一些正在建设中的基础工作。因此尚未最终确定。

延斯·古斯特

set_bit在展开_SFR_BIT之前,将参数分解为几个因此,当它看到由后者扩展引起的逗号时,已经为时已晚。

一个常见的技巧是再次扩大论点

#define set_bit0(...)   set_bit(__VA_ARGS__)
#define set_output(pin) set_bit0(_PIN(pin))

在这里_PIN(pin)展开并传递到set_bit0从中传递参数时set_bit0set_bit会看到已经扩展的序列,包括逗号。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

/ proc / cpuinfo文件告诉我有关硬件的什么信息?

该调试信息告诉我有关尝试的SSH连接出了什么问题的信息?

为什么有时宏扩展会增加空间?

为什么即使我使用[[fallthrough]],GCC也会警告我有关失败的消息?

为什么在使用继承的构造函数时,GCC会警告我有关无用的强制转换?

为什么STS会警告我有关不匹配的参数?

Maven为什么警告我有关编码的信息?

为什么此宏没有扩展?

当我明确处理Chrome时,为什么Chrome会告诉我我有未处理的拒绝?

我是React的新手,有人可以告诉我为什么会这样吗?

当我尝试推向起点时,为什么Git会告诉我“没有这样的远程'起点'”?

谁能告诉我有关Android中的InternalStorage的信息?我很困惑

OSX上的netstat -r告诉您有关网关的什么信息?

为什么Chrome告诉我有警告消息,却不告诉我它们是什么?

java -version是否告诉我有关JRE或JDK的信息?

Git fetch反复告诉我有关同一新分支的信息

谁能告诉我有关Django的延迟加载和事务处理的信息?

为什么 NUnit 告诉我“没有提供足够的参数,至少提供 2 个参数。” 当我提供 2 个参数时?

为什么Quarkus会警告我有关私有领域的注入?

当我尝试 git remote remove origin 时,为什么 Git 会告诉我“错误:没有这样的远程 'origin'”?

函数文档缺少有关返回的指针为NULL或非NULL的信息。假设什么?

调用泛型函数(具有通过接口实现的约束)会产生有关缺少约束的错误。我看不到什么?

为什么NumPy-C api不会警告我有关分配失败的信息?

为什么Visual Studio不警告我有关空引用异常的信息?

SceneKit统计信息窗口告诉我们什么?

详细信息未显示 - 有人可以告诉我我的代码有什么问题吗?

为什么 Intellij 会向我显示有关弹簧配置文件的警告?

你能告诉我我用来检索客户上次下订单信息的查询有什么问题吗?

为什么 Python assertEqual 告诉我我在这个单元测试中缺少第二个参数?