【发布时间】:2025-12-20 08:10:12
【问题描述】:
在嵌入式系统中,您通常有一个内存位置,它不在程序内存本身内,而是指向一些硬件寄存器。大多数 C SDK 将这些作为#define 语句提供。根据以下文章,https://arne-mertz.de/2017/06/stepping-away-from-define/ 一种从 #define 语句(由 C SDK 使用)转换为对 C++ 更友好的方法是创建一个强制 reinterpret_cast 在运行时发生的类。
我试图以稍微不同的方式来解决这个问题,因为我希望能够为不同的指针创建“类型特征”。让我用一个例子来说明。
#define USART1_ADDR 0x1234
#define USART2_ADDR 0x5678
template <typename T_, std::intptr_t ADDR_>
class MemPointer {
public:
static T_& ref() { return *reinterpret_cast<T_*>(ADDR_); }
};
class USART {
public:
void foo() { _registerA = 0x10; }
private:
uint32_t _registerA;
uint32_t _registerB;
};
using USART1 = MemPointer<USART, USART1_ADDR>;
using USART2 = MemPointer<USART, USART2_ADDR>;
template <typename USART_>
class usart_name;
template <>
class usart_name<USART1> {
public:
static constexpr const char* name() { return "USART1"; }
};
template <>
class usart_name<USART2> {
public:
static constexpr const char* name() { return "USART2"; }
};
此示例中的每个 USART“实例”都是其自己的唯一类型,因此我能够创建特征,允许在编译时“查找”有关 USART 实例的信息。
这实际上似乎工作,但是,我想创建一些测试代码如下
static USART testUsart;
#define TEST_USART_ADDR (std::intptr_t)(&testUsart);
using TEST_USART = MemPointer<USART, TEST_USART_ADDR>;
失败并出现以下错误:
从指针类型'USART*'到算术类型的转换 'intptr_t' {aka 'long long int'} 在常量表达式中
我相信我根据Why is reinterpret_cast not constexpr?了解问题的根源
我的问题是,有没有办法让我的 MemPointer 模板也适用于上面的测试代码?
编辑
一种解决方案是为每个“实例”设置一个单独的类
class USART1 : public USART {
public:
static USART& ref() { return *reinterpret_cast<USART*>(USART1_ADDR); }
};
class USART2 : public USART {
public:
static USART& ref() { return *reinterpret_cast<USART*>(USART2_ADDR); }
};
我更喜欢某种模板+使用组合,但我不需要编写一堆类。但也许这是唯一的选择。
【问题讨论】:
-
你能把它改成:
static constexpr USART testUsart;吗? -
您是否有令人信服的理由将这些地址设为模板参数,而不是运行时参数?您的问题似乎集中在使变量的运行时地址成为编译时常量。
-
无关:为什么用
#defines 而不是constexpr std::uintptr_t USART1_ADDR = 0x1234;constexpr std::uintptr_t USART2_ADDR = 0x5678;? -
@TedLyngmo 定义来自嵌入式系统制造商提供的 C SDK。基本上,我正在围绕一些已经提供的代码编写一个轻量级的 C++ 层。但我同意,理想情况下你会直接使用 constexpr。
-
@DrewDormann 你的措辞很有意义。本质上,外设地址也是运行时的。我最初为每个单独的 USART“实例”创建了一个单独的类,并使用一个静态 instance() 函数返回地址的转换版本。我试图将其作为模板来避免所有重复的类代码。