ARM C++编译器不会编译具有可变常量成员的结构

ARM C++ compiler won't compile structure with volatile const member

本文关键字:常量 成员 结构 编译器 C++ 编译 ARM      更新时间:2023-10-16

我正在尝试为 ARM MCU 目标编译C++代码,其中包括一个寄存器定义头文件。我收到以下错误:

test.h(18): error:  #294: invalid union member -- class "struct <unnamed>::<unnamed>::<unnamed>" has a disallowed member function

该错误仅在编译 C++ 项目时发生,而不是在编译 C 项目时发生。

它是由以下结构的__IM(volatile const(成员引起的:

typedef struct {
union {
__IOM uint32_t VAR;
struct {
__IOM uint32_t BIT0  : 1;
__IOM uint32_t BIT1  : 1;
__IOM uint32_t BIT2  : 1;
__IM  uint32_t TBD   : 13;
__IOM uint32_t BIT17 : 1; 
} VAR_b;
} ;
} VAR_Type;

这里解释了为什么这些__IM成员应该是volatile const,即使在C++:

http://www.keil.com/support/docs/3687.htm

我正在使用带有默认ARM编译器的Keil uVision:

*** Using Compiler 'V5.06 update 6 (build 750)', folder: 'C:Keil_v5ARMARMCCBin'

如果不破解MCU.h文件以重新定义__IMvolatile(不是volatile const(,我找不到解决此问题的方法

#undef __IM
#define __IM volatile

添加前面的代码解决了问题并允许我进行编译,但 MCU 制造商提供的MCU.h文件不应该被修改,所以我想找到一个更干净的解决方案。

您看到的错误与 g++ 在 C++03 模式下输出的错误最相似。即,对于以下代码

#include <stdint.h>
#define __IOM volatile
#define __IM volatile const
typedef struct {
union {
__IOM uint32_t VAR;
struct {
__IOM uint32_t BIT0  : 1;
__IOM uint32_t BIT1  : 1;
__IOM uint32_t BIT2  : 1;
__IM  uint32_t TBD   : 13;
__IOM uint32_t BIT17 : 1; 
} VAR_b;
} ;
} VAR_Type;
int main()
{
VAR_Type x={0};
}

我得到以下编译结果:

$ g++ test.cpp -o test -pedantic-errors -std=c++03
test.cpp:15:7: error: member ‘<unnamed struct>::<unnamed union>::<unnamed struct> <unnamed struct>::<unnamed union>::VAR_b’ with copy assignment operator not allowed in union
} VAR_b;
^~~~~
test.cpp:15:7: note: unrestricted unions only available with -std=c++11 or -std=gnu++11

问题是联合内部的结构没有任何复制赋值运算符,因为它的格式不正确(不能分配给const成员(。有关详细信息,请参阅此 SO 答案。由于(我想(您无法编辑标头,因此您可能希望在 C++11 模式下使用启用了 C++11 的编译器。不确定凯尔是否有。

我无权访问 Keil 编译器,但在 g++ 7.4.0 下,此结构生成的错误不是在定义它的地方,而是在实例化它的地方。

即 该行

VAR_Type fred;

导致错误,因为const成员需要初始化器(作为位字段,不能指定为默认初始化器,因此只能通过默认构造函数定义提供(。

你自己实例化这个结构吗?(也许在第 18 行?

如果是,并且您这样做是出于合法原因(例如,为了硬件寄存器的内存副本(,似乎确实有一个解决方案:

VAR_Type fred = { 0 };

编译没有错误(至少使用 G++ 7.4.0(。 (请注意,这通过联合的第一个成员初始化结构。

[编辑添加:一个包含完整编译器命令行的最小完整可验证示例将使人们更容易帮助您。

非常感谢您的帮助!

我可以联系到 ARM 支持。这似乎是他们的SVDconv工具和ARM编译器v5(又名"armcc"(生成的头文件之间不兼容的真正问题。解决方案是使用 ARM 编译器 v6(又名"armclang"(构建项目。

这些结构不是用户实例化的,因为它们是普通的 C。它们允许轻松访问MCU寄存器。以下是如何在制造商提供的标头中声明它们的示例:

typedef struct {                                /*!< (@ 0x42000000) GPIOA Structure                                            */
union {
__IOM uint32_t CTRL;                        /*!< (@ 0x00000000) Control register                                           */
struct {
__IM  uint32_t RES        : 1;
__IOM uint32_t RST        : 1;            /*!< [1..1] Soft reset                                                         */
__IOM uint32_t INTEN      : 1;            /*!< [2..2] Interrupt enable                                                   */
} CTRL_b;
} ;
union {
__IOM uint32_t IRQPOLSET;                   /*!< (@ 0x00000004) Interrupt polarity set register                            */
struct {
__IOM uint32_t P0         : 1;            /*!< [0..0] Pin 0                                                              */
__IOM uint32_t P1         : 1;            /*!< [1..1] Pin 1                                                              */
__IOM uint32_t P2         : 1;            /*!< [2..2] Pin 2                                                              */
__IOM uint32_t P3         : 1;            /*!< [3..3] Pin 3                                                              */
__IOM uint32_t P4         : 1;            /*!< [4..4] Pin 4                                                              */
__IOM uint32_t P5         : 1;            /*!< [5..5] Pin 5                                                              */
__IOM uint32_t P6         : 1;            /*!< [6..6] Pin 6                                                              */
__IOM uint32_t P7         : 1;            /*!< [7..7] Pin 7                                                              */
__IOM uint32_t P8         : 1;            /*!< [8..8] Pin 8                                                              */
__IOM uint32_t P9         : 1;            /*!< [9..9] Pin 9                                                              */
__IOM uint32_t P10        : 1;            /*!< [10..10] Pin 10                                                           */
__IOM uint32_t P11        : 1;            /*!< [11..11] Pin 11                                                           */
__IOM uint32_t P12        : 1;            /*!< [12..12] Pin 12                                                           */
__IOM uint32_t P13        : 1;            /*!< [13..13] Pin 13                                                           */
__IOM uint32_t P14        : 1;            /*!< [14..14] Pin 14                                                           */
__IOM uint32_t P15        : 1;            /*!< [15..15] Pin 15                                                           */
} IRQPOLSET_b;
} ;
[etc...]
} GPIO_Type;

下面是一个如何定义第一个对应寄存器的物理地址的示例:

#define GPIOA_BASE                  0x41000000UL

这是用户将在其代码中使用的名称的链接:

#define GPIOA                       ((GPIO_Type*)              GPIOA_BASE)

因此,为了访问 IRQPOLSET 电阻器的位 P10,他这样做:

GPIOA->IRQPOLSET_b.P10