Pat*_* B. 16 c++ embedded c++11
在嵌入式世界中,人们将硬件(-configuration)-register-mappings编写为结构,这是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;
}
Run Code Online (Sandbox Code Playgroud)
这非常有效,编译器(gcc至少在我们的平台上)认识到它hw_reg
引用了相同的地址(在编译时已知并且是常量)并且ld
仅仅是一次.第二个st
(存储)使用单个指令完成4字节偏移 - 再次在我们的平台上.
如何使用现代C++(后C++ 11)重现这种行为而不使用#defines
?
我们尝试了很多东西:static const
课堂内外constexpr
.他们都不喜欢(隐含)reinterprest_cast<>
.
回应关于为什么改变它的评论:我担心它主要是名利双收.但不仅如此.有了这个C代码调试可能很难.想象一下,你想要记录所有的写访问,这种方法需要你重写所有的地方.但是,在这里,我不是在寻找能够简化特定情况的解决方案,我正在寻找灵感.
编辑只是为了澄清一些评论:我要求这个问题不要改变任何有效的代码(并且写于20世纪90年代).我正在寻找未来项目的解决方案,因为我对define
-implementation 并不完全满意,并且问自己现代C++是否具有更高的可能性.
我认为变量模板可以在这里提供优雅的解决方案.
// 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);
}
Run Code Online (Sandbox Code Playgroud)
像这样使用它而不是使用宏的一个好处是,你是类型安全的,并且你实际上可以从同一个源文件中引用不同硬件模块的寄存器而不需要将它们全部混合起来.