将 IO 寄存器作为模板参数传递

bri*_*ore 2 c++ templates avr c++11

我想使用 IO 寄存器(== 静态内存地址)作为模板参数。问题是,寄存器通常被定义为宏扩展到类似的东西(*(volatile uint8_t*)(11 + 0x20)),我不知何故无法正确使用我的模板。

我想编写如下代码:

Foo<PORTB> foo;
Run Code Online (Sandbox Code Playgroud)

这样我就可以轻松更改类使用的 IO 寄存器,而无需任何运行时开销(这对微控制器至关重要)。我在下面包含了一个完整的例子:

#include <stdint.h>
#include <stdio.h>
#include <utility>

#define PORTB  (*(volatile uint8_t*)(11 + 0x20))

template<volatile uint8_t* PortRegister>
class ControllerPtr final
{
public:
    static void SetHigh() { *PortRegister |= 0x2; }
};

template<volatile uint8_t& PortRegister>
class ControllerRef final
{
public:
    static void SetHigh() { PortRegister |= 0x2; }
};


int main()
{
    ControllerPtr<&PORTB> ptr;
    ControllerRef<PORTB> ref;

    ptr.SetHigh();
    ref.SetHigh();

    // Both statements should be equal to:
    // PORTB |= 0x2;
}
Run Code Online (Sandbox Code Playgroud)

每次当我尝试传递&PORTB到 时ControllerPtr,g++ 都无法编译:

错误:(volatile uint8_t*)((long int)(11 + 32))不是有效的模板参数,volatile uint8_t* {aka volatile unsigned char*}因为它不是变量的地址

错误:表达式*(volatile uint8_t*)((long int)(11 + 32))有副作用

尝试传递PORTB给像 in 中使用的引用类型时,错误有点不同ControllerRef

错误:*(volatile uint8_t*)((long int)(11 + 32))不是类型的有效模板参数,volatile uint8_t& {aka volatile unsigned char&}因为它不是具有链接的对象

我实际上不明白为什么这个错误是一个错误,因为我没有看到将静态地址传递给模板的任何问题。

Bri*_*ian 5

非类型模板参数必须是常量表达式,并且不能reinterpret_cast在常量表达式中包含 a (除非它未求值)。

由于您已经指出除了通过宏(如 )之外,您无法访问数字地址PORTB,因此我建议采用一种解决方法。虽然PORTB不能在模板参数中使用,但我们可以合成一个可以在模板参数中使用的唯一类型,如下所示:

struct PORTB_tag {
    static volatile uint8_t& value() { return PORTB; }
};
template <class PortTag>
class ControllerRef final {
  public:
    static void SetHigh() { PortTag::value() |= 0x2; }
};
int main() {
    ControllerRef<PORTB_tag> ref;
    ref.SetHigh();
}
Run Code Online (Sandbox Code Playgroud)

当你有很多端口时,为了避免重复输入,我们可以使用宏:

#define PORT_TAG(port) port_tag_for_ ## port
#define MAKE_PORT_TAG(port) struct port_tag_for_ ## port { \
    static volatile uint8_t& value() { return port; } \
}
template <class PortTag>
class ControllerRef final {
  public:
    static void SetHigh() { PortTag::value() |= 0x2; }
};
MAKE_PORT_TAG(PORTB);
int main() {
    ControllerRef<PORT_TAG(PORTB)> ref;
    ref.SetHigh();
}
Run Code Online (Sandbox Code Playgroud)

http://coliru.stacked-crooked.com/a/401c0847d77ec0e0