将 constexpr const 指针指向嵌入式应用程序的易失性内存位置的现代 C++ 方式是什么?

Cam*_*ind 9 casting volatile c++11 c++17

在构建用于控制嵌入式微处理器上的硬件的库时,一项常见任务是操作特定内存位置处的位以控制硬件功能。

在 AVR 处理器中,Atmel(现在的 Microchip)提供了扩展为如下的宏:

#define PORTA (*(volatile uint8_t *)(0x25))
Run Code Online (Sandbox Code Playgroud)

这可以实现以下功能:

PORTA |= 1;
Run Code Online (Sandbox Code Playgroud)

现在,在 C++11(及更新版本)中,希望将几乎所有用法替换#defineconstexpr

在旧版本的 GCC C++ 编译器 (4.9.2) 中,编译了以下内容:

#include <avr/io.h>
constexpr volatile uint8_t *const PortA = &PORTA;
Run Code Online (Sandbox Code Playgroud)

在8.2.0版本中,上面的代码无法编译并给出错误:

error: 'reinterpret_cast<volatile uint8_t* {aka volatile unsigned char*}>(37)' is not a constant expression
Run Code Online (Sandbox Code Playgroud)

我不是在寻找解释为什么不能reinterpret_castconstexpr上下文中使用或者为什么整数到指针的转换是非法的。

在现代 C++ 中拥有constexpr指向内存的指针的正确方法是什么?volatile

我见过一些建议,将 a 的内存地址存储PORTA在 a 中constexpr uintptr_t,然后在运行时reinterprect_cast进行位volatile uint8_t * const操作。

例如,这可以工作,甚至可以按预期编译为单个sbi指令。avr-gcc

#include <stdint.h>
constexpr uintptr_t PortA = 0x25;
void set() { *((volatile uint8_t *)(PortA)) |= 1; }
Run Code Online (Sandbox Code Playgroud)

然而,它需要相当数量的丑陋的样板才能用作PortA它的预期指针。

这也存在一个问题,就是似乎无法PORTA直接使用宏。相反,我们被迫对内存地址进行硬编码0x25,这破坏了某些理想的可移植性功能。

感觉就像我错过了一些明显的东西,因为我的搜索没有产生任何成果。

例如,这感觉像是一个“地址常量表达式”,但这似乎与引用静态分配的值有关,const这并不完全是我想要的。

const char str[] = "FooBar";
constexpr const char * x = str + 2;
Run Code Online (Sandbox Code Playgroud)

eca*_*mur 0

您无法创建由非常量表达式初始化的 constexpr 指针。但是,您可以创建一个static const指针:

static uint8_t volatile* const PortA = &PORTA;
Run Code Online (Sandbox Code Playgroud)

或者,更好的是,static参考:

static uint8_t volatile& PortA = PORTA;
Run Code Online (Sandbox Code Playgroud)