在嵌入式世界中,人们编写硬件(-configuration)-寄存器-映射作为结构,这是32位硬件的一个非常简单的示例:
#define hw_baseaddr ((uintptr_t) 0x10000000)
struct regs {
uint32_t reg1;
uint32_t reg2;
};
#define hw_reg ((volatile struct regs *) hw_baseaddr)
void f(void)
{
hw_reg->reg1 = 0xdeadcafe;
hw_reg->reg2 = 0xc0fefe;
}
这非常有效,编译器(至少在我们的平台上是gcc)识别出hw_reg
引用了相同的地址(这个地址在编译时是已知的并且是常量),并且只存储它一次。第二个ld
(存储)是用一个4字节的偏移量和一条指令来完成的-同样是在我们的平台上。
如何在不使用#defines
的情况下使用现代C++ (post C++11)重现此行为
我们尝试了很多东西:类内部和外部的static const
以及constexpr
。他们都不喜欢(隐含的)reinterprest_cast<>
。
在回应为什么要改变它的评论时:我担心这主要是为了名声和荣誉。但不仅仅是这样。有了这个C代码,调试可能会很困难。假设您想要记录所有的写访问,这种方法将要求您在任何地方重写所有内容。然而,在这里,我不是在寻找一个可以简化特定情况的解决方案,我是在寻找灵感。
根据一些评论,编辑只是为了澄清:我问这个问题是为了不要更改任何正在运行的代码(并且是在20世纪90年代编写的)。我正在为未来的项目寻找解决方案,因为我对define
-implementation并不完全满意,并问自己现代C++是否有更好的可能性。
发布于 2017-09-20 18:44:50
我认为可变模板在这里是一个优雅的解决方案。
// Include this in some common header
template <class Impl>
volatile Impl& regs = *reinterpret_cast<volatile Impl*>(Impl::base_address);
template <std::uintptr_t BaseAddress>
struct HardwareAt {
static const std::uintptr_t base_address = BaseAddress;
// can't be instantiated
~HardwareAt() = delete;
};
// This goes in a certain HW module's header
struct MyHW : HardwareAt<0x10000000> {
std::uint32_t in;
std::uint32_t out;
};
// Example usage
int main()
{
std::printf("%p\n%p\n", ®s<MyHW>.in, ®s<MyHW>.out);
// or with alias for backward compatibility:
auto hw_reg = ®s<MyHW>;
std::printf("%p\n%p\n", &hw_reg->in, &hw_reg->out);
}
这样使用它而不是宏的一个好处是,您是类型安全的,并且您实际上可以引用来自同一源文件的不同硬件模块的寄存器,而不会混淆它们。
发布于 2017-09-22 08:10:03
由于#define
的唯一目的是使您能够访问结构成员,因此您可以使用模板来执行相同的操作。我的编译器为模板生成与#define
相同的代码。
// #define hw_reg ((volatile struct regs *) hw_baseaddr)
template <class T, uintptr_t addr>
class RegsPtr
{
public:
RegsPtr() { ; }
volatile T* operator->() const { return reinterpret_cast<T*>(addr); }
volatile T& operator*() const { return *operator->(); }
};
const RegsPtr<struct regs, hw_baseaddr> hw_reg;
https://stackoverflow.com/questions/46317994
复制相似问题