нетиповые параметры шаблона на основе аппаратных регистров

Я использую аппаратные таймеры 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 *»

ошибка | «T» не является допустимым аргументом шаблона, потому что «t» является переменной, а не адресом переменной

После прочтения о параметрах нетипичных шаблонов я все еще не уверен, как решить мою проблему и можно ли использовать шаблоны так, как я думаю. Я попробовал static_cast и reinterpret_cast в варианте 1, но, похоже, это не имело никакого значения.

3

Решение

Укороченная версия

Типовой аргумент не должен быть константным выражением. ((timer_peripheral *) 0x40000000) включает в себя reinterpret_cast к типу указателя, поэтому вы не можете использовать его в константном выражении.


C ++ 03

[Temp.arg.nontype] / 1

Шаблонный аргумент для нетипового, нешаблонного шаблона-параметра должен быть одним из:

  • интегральная константа-выражение целочисленного или перечислимого типа; или же
  • […]
  • адрес объекта или функции с внешней связью, включая шаблоны функций и функции
    шаблоны-идентификаторы, но исключая нестатические члены класса, выраженные как & id-expression где & необязательно, если имя ссылается на функцию или массив, или если соответствующий параметр шаблона является ссылкой; или же
  • […]

Поэтому мы должны использовать интегральное константное выражение.

[Expr.const] / 1

Интегральное выражение-константа может включать только литералы (2.13), перечислители, константные переменные или члены-статические данные целочисленных или перечислимых типов, инициализированных с помощью константных выражений (8.5), нетипичные параметры шаблона целочисленных или перечислимых типов и размер выражений. […] Можно использовать только преобразования типов в целочисленные или перечислимые типы.

((timer_peripheral *) 0x40000000) включает приведение к типу указателя, поэтому оно не может появляться в интегральном константном выражении.


C ++ 11

[Temp.arg.nontype] / 1

Шаблонный аргумент для нетипового, нешаблонного шаблона-параметра должен быть одним из:

  • для нетипового шаблона-параметра целочисленного типа или типа перечисления преобразованное константное выражение (5.19) типа шаблона-параметра; или же
  • […]
  • константное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженную (игнорирование круглые скобки) как & 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;
}
1

Другие решения

Параметр шаблона должен быть целочисленным типом; Я думаю, что компилятор приводит его к unsigned int.

Вы могли бы, вероятно, сделать эту работу, запустив ее и явно приведя ее в соответствие с шаблоном, но я не думаю, что вам это нужно.

Если нет какой-либо причины, по которой вам действительно нужно это шаблонировать, я бы просто (1) добавил методы в структуру; или (2) создать класс-оболочку, который просто содержит указатель на структуру.

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;

}
0
По вопросам рекламы [email protected]