注册为模板参数

时间:2015-02-19 10:59:16

标签: c++ gcc embedded

我正在寻找一种使用gcc 4.8.4将嵌入式设备寄存器传递给C ++模板的方法。在描述嵌入式器件的数据手册中,寄存器的地址通常作为原始存储器位置给出(例如,0x40008000)。

当我测试软件时,我想使用静态整数作为寄存器来查看寄存器值是否设置正确。

所以基本上一个设备外围设备的包装器归结为一个类,其寄存器地址作为模板参数给出:

template < volatile std::uint32_t* Reg >
struct peripheral {};

测试工作正常:

std::uint32_t reg;
peripheral< &reg > mocked;

但是当我想用固定的数据表给出地址时实例化模板:

peripheral< reinterpret_cast< std::uint32_t* >( 0x40008000 ) > mocked;

gcc抱怨:could not convert template argument '1073774592u' to 'volatile uint32_t* {aka volatile long unsigned int*}。 clang并没有抱怨这个。

如果我使用给定为整数的地址作为模板参数,我在测试期间使用模拟寄存器的地址实例化模板时遇到问题:

template < std::intptr_t Reg >
struct peripheral {};

std::uint32_t reg;
peripheral< reinterpret_cast< std::intptr_t >( &reg ) > mocked;

这会产生error: conversion from pointer type 'uint32_t* {aka long unsigned int*}' to arithmetic type 'intptr_t {aka int}' in a constant-expression

我可以想到两个解决方案:

1)使用指针作为模板参数,使用全局变量作为寄存器,并使用一些链接器脚本魔法来修复寄存器的地址。

2)使用特殊寄存器类型,这些寄存器类型具有与外围模板的通用接口,但有两种非常不同的实现用于测试和实际应用。

但我正在寻找一种更简单的方法来实现这一目标。任何想法,指针或评论?

2 个答案:

答案 0 :(得分:2)

在常量表达式中不允许执行reinterpret_cast<>(编译器也会告诉您);另见Constant expressions

我建议如下(另见C++11 constexpr function's argument passed in template argument):

#include <cstdint>
#include <cassert>

template<typename PtrT, PtrT Ptr>
struct peripheral
{
  static void* const value;

  template<typename T>
  static T* as() noexcept
  { return static_cast<T*>(value); }
};

#define PERIPHERAL(addr) peripheral<decltype((addr)), (addr)>

std::uint32_t reg = 0;

int main() {
   peripheral<std::uintptr_t, 0x42> mocked1;
   peripheral<volatile std::uint32_t*, &reg> mocked2;
   PERIPHERAL(0x42) mocked3;
   PERIPHERAL(&reg) mocked4;
   assert((mocked3.value == PERIPHERAL(0x42)::as<void*>()));
   return 0;
}

答案 1 :(得分:0)

我的解决方案如下:

template < class Name = int, typename T = std::uint32_t, T* Value = nullptr >
class mocked_register
{
public:
    static void set( T v )
    {
        *address() = v;
    }

    static T get()
    {
        return *address();
    }

    static volatile T* address()
    {
        static T internal_;
        return Value ? Value : &internal_;
    }
};

其中Name应该是使实例化与其他实例化不同的任何类型。定义多个类型时,可以使用以前定义的类型作为名称:

typedef mocked_register<>             START;
typedef mocked_register< START >      STOP;
typedef mocked_register< STOP >       COUNT;

对于测试,该类型保留一个“半”静态变量以保持寄存器值。对于arm架构,我有一些使用寄存器数组的情况。在这种情况下,Value参数可用于提供外部数组:

std::uint32_t capture_regs[ 4 ];
typedef mocked_register< SHUTDOWN, std::uint32_t, capture_regs > CAPTURE;

对于生产部件,模板更容易:

template < std::uint32_t Register >
struct addressed_register
{
    static void set( std::uint32_t value )
    {
        *reinterpret_cast< volatile std::uint32_t* >( Register ) = value;
    }

    static std::uint32_t get()
    {
        return *reinterpret_cast< volatile std::uint32_t* >( Register );
    }
};

在这两种情况下(测试和生产),设备抽象采用一组模板参数并将它们用作寄存器:

template <
    class OUTSET,
    class OUTCLEAR,
    class DIRSET,
    class DIRCLR,
    class PIN_CNF,
    std::uint8_t  Nr >
struct pin
{
    static void init_as_input()
    {
        DIRCLR::set( 1 << Nr );
    }
};

如果实现赋值和隐式转换为T,可以添加更多类似语法的寄存器(但我不是这个想法的忠实粉丝):

START start;
COUNT count;
start = 1;
std::uint32_t c = count;