3

我正在使用 STM32 的硬件计时器与许多光学编码器连接。我想创建一个模板化类,它为与硬件计时器交互提供了一个方便的接口。定时器寄存器是存储器映射的,它们的地址在制造商提供的与器件数据表相匹配的标头中定义。模板参数实际上是定时器外设的基本内存地址。下面是我目前正在尝试做的一个最小的工作示例:

#include <cstdint>

// Effectively supplied by chip manufacturer headers
struct timer_peripheral {
  volatile uint32_t count;
  // ... lots of other registers ...
};
// Also supplied by chip manufacturer headers
#define TIM1 ((timer_peripheral *) 0x40000000)
#define TIM2 ((timer_peripheral *) 0x40000400)
// My templated class
template <timer_peripheral * Timer>
class OpticalEncoderCounter {
  OpticalEncoderCounter();
};

template <timer_peripheral * Timer>
OpticalEncoderCounter<Timer>::OpticalEncoderCounter()
{
}

int main()
{
  // option 1
  OpticalEncoderCounter<TIM1> encoder0;

  // option 2
  timer_peripheral * t = TIM2;
  OpticalEncoderCounter<t> encoder1;
}

但是,当我编译时,我使用 g++-4.7.2 -std=c++11 得到这些错误:

错误| 无法将模板参数“1073742848u”转换为“timer_peripheral*”</p>

错误| 't' 不是有效的模板参数,因为 't' 是一个变量,而不是变量的地址

在阅读了有关非类型模板参数的信息后,我仍然不确定如何解决我的问题以及是否可以按照我的想法使用模板。我在选项 1 中尝试了 static_cast 和 reinterpret_cast,但似乎没有任何区别。

4

3 回答 3

1

精简版

模板非类型参数必须是常量表达式。((timer_peripheral *) 0x40000000)包含reinterpret_cast指向指针类型的 a,因此您不能在常量表达式中使用它。


C++03

[temp.arg.nontype]/1

非类型、非模板模板参数的模板参数应为以下之一:

  • 整数或枚举类型的整数常量表达式;或者
  • [...]
  • 具有外部链接的对象或函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,如果名称指代函数或数组,或者对应的模板参数& id-expression,则表示为可选&是参考;或者
  • [...]

所以我们必须使用一个整数常量表达式。

[expr.const]/1

一个整型常量表达式只能包含字面量 (2.13)、枚举数、常量变量或使用常量表达式初始化的整型或枚举类型的静态数据成员 (8.5)、整型或枚举类型的非类型模板参数,以及 sizeof 表达式。[...]只能使用到整数或枚举类型的类型转换。

((timer_peripheral *) 0x40000000)包括对指针类型的强制转换,因此它不能出现在整型常量表达式中。


C++11

[temp.arg.nontype]/1

非类型、非模板模板参数的模板参数应为以下之一:

  • 对于整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或者
  • [...]
  • 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号) as & id-expression,但&如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或者
  • [...]

我们不能使用“设计地址...”的常量表达式,但我们可以使用转换后的常量表达式吗?

[expr.const]/2

条件表达式是核心常量表达式,除非它涉及以下之一 [...]

  • [...]
  • reinterpret_cast (5.2.10);
  • [...]

没有。不可能。


一种解决方法

使用函数模板返回指针。

#include <cstdint>

// Effectively supplied by chip manufacturer headers
struct timer_peripheral {
    volatile uint32_t count;
    // ... lots of other registers ...
};

#define TIM1 ((timer_peripheral *) 0x40000000)
#define TIM2 ((timer_peripheral *) 0x40000400)

enum TIMS { tim1, tim2 };

template < TIMS tim >
inline timer_peripheral* get_timer_address()
{
    static_assert(tim && false, "unknown timer identifier");
    return nullptr;
}
  template <>
  inline timer_peripheral* get_timer_address < tim1 >()
  {
      return TIM1;
  }
  template <>
  inline timer_peripheral* get_timer_address < tim2 >()
  {
      return TIM2;
  }

// My templated class
template < TIMS tim >
class OpticalEncoderCounter {
    static timer_peripheral* get() { return get_timer_address<tim>(); }

public:
    OpticalEncoderCounter();
};

template < TIMS tim >
OpticalEncoderCounter<tim>::OpticalEncoderCounter()
{
}

int main()
{
    OpticalEncoderCounter<tim1> encoder0;
}
于 2013-05-15T22:39:43.613 回答
0

使用指针算术将指针转换为常量整数:

// My templated class


#define TIM_BASE TIM1

const int TIM1_OFFSET = TIM1 - TIM_BASE;

template <size_t timerOffset>
class OpticalEncoderCounter {
public:
  OpticalEncoderCounter();

  timer_peripheral * getTimer() { return  TIM_BASE + timerOffset;}

};

template <size_t timerOffset>
OpticalEncoderCounter<timerOffset>::OpticalEncoderCounter()
{

}


int main()
{

  OpticalEncoderCounter<TIM1_OFFSET> encoder0;

}
于 2013-05-15T23:27:02.953 回答
0

模板参数必须是整数类型;我认为编译器将其转换为无符号整数。

你可能可以通过运行它来完成这项工作,并在模板类中自己显式地转换它,但我认为你不需要这样做。

除非出于某种原因您确实需要将其模板化,否则我只会(1)向结构添加方法;或 (2) 创建一个仅包含指向该结构的指针的包装类。

于 2013-05-15T22:26:44.393 回答