Я использую аппаратные таймеры 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, но, похоже, это не имело никакого значения.
Типовой аргумент не должен быть константным выражением. ((timer_peripheral *) 0x40000000)
включает в себя reinterpret_cast
к типу указателя, поэтому вы не можете использовать его в константном выражении.
Шаблонный аргумент для нетипового, нешаблонного шаблона-параметра должен быть одним из:
- интегральная константа-выражение целочисленного или перечислимого типа; или же
- […]
- адрес объекта или функции с внешней связью, включая шаблоны функций и функции
шаблоны-идентификаторы, но исключая нестатические члены класса, выраженные как& id-expression
где&
необязательно, если имя ссылается на функцию или массив, или если соответствующий параметр шаблона является ссылкой; или же- […]
Поэтому мы должны использовать интегральное константное выражение.
[Expr.const] / 1Интегральное выражение-константа может включать только литералы (2.13), перечислители, константные переменные или члены-статические данные целочисленных или перечислимых типов, инициализированных с помощью константных выражений (8.5), нетипичные параметры шаблона целочисленных или перечислимых типов и размер выражений. […] Можно использовать только преобразования типов в целочисленные или перечислимые типы.
((timer_peripheral *) 0x40000000)
включает приведение к типу указателя, поэтому оно не может появляться в интегральном константном выражении.
Шаблонный аргумент для нетипового, нешаблонного шаблона-параметра должен быть одним из:
- для нетипового шаблона-параметра целочисленного типа или типа перечисления преобразованное константное выражение (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;
}
Параметр шаблона должен быть целочисленным типом; Я думаю, что компилятор приводит его к unsigned int.
Вы могли бы, вероятно, сделать эту работу, запустив ее и явно приведя ее в соответствие с шаблоном, но я не думаю, что вам это нужно.
Если нет какой-либо причины, по которой вам действительно нужно это шаблонировать, я бы просто (1) добавил методы в структуру; или (2) создать класс-оболочку, который просто содержит указатель на структуру.
Преобразуйте указатели в постоянные целые числа, используя арифметику указателей:
// 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;
}