У меня есть два класса, которые я использую для представления некоторых аппаратных средств: Кнопка и класс InputPin, которые представляют кнопку, которая изменит значение входного контакта IC, когда это будет придавлено. Простой пример их:
template <int pinNumber> class InputPin
{
static bool IsHigh()
{
return ( (*portAddress) & (1<<pinNumber) );
}
};
template <typename InputPin> class Button
{
static bool IsPressed()
{
return !InputPin::IsHigh();
}
};
Это работает красиво и при помощи шаблонов классов, условие ниже скомпилирует как плотно, как будто я написал от руки его в блоке (единственная инструкция).
Button < InputPin<1> > powerButton;
if (powerButton.IsPressed())
........;
Однако я расширяю его, чтобы иметь дело с прерываниями и иметь проблему с циклическими ссылками.
По сравнению с исходным InputPin новый класс InputPinIRQ имеет дополнительную статическую функцию членства, которую назовут автоматически аппаратные средства, когда значение контакта изменится. Я хотел бы, чтобы это смогло уведомить класс Кнопки относительно этого, так, чтобы класс Кнопки мог затем уведомить главное приложение, что это было нажато/выпущено. Я в настоящее время делаю это передающими указателями на функции обратного вызова. Для кода обратного вызова, который будет встроен компилятором, я полагаю, что это должно передать эти указатели функции как шаблонные параметры. Таким образом, теперь оба из новых классов имеют дополнительный шаблонный параметр, который является указателем на функцию обратного вызова. К сожалению, это дает мне циклическую ссылку, потому что для инстанцирования класса ButtonIRQ я теперь должен сделать что-то вроде этого:
ButtonIRQ<InputPinIRQ<1, ButtonIRQ< Inp.... >::OnPinChange>, OnButtonChange> pB;
где...... представляет циклическую ссылку.
Кто-либо знает, как я могу избежать этой циклической ссылки? Я плохо знаком с шаблонами, так вероятно, пропускаю что-то действительно простое.
PS важно, чтобы компилятор знал точно, какой код будет выполнен, когда прерывание произойдет как он затем, делает некоторую очень полезную оптимизацию - это может встроить функцию обратного вызова и буквально вводит код функции обратного вызова в точном адресе, который называют на прерывании h/w.
Я бы изменил дизайн так, чтобы кнопка была связана с входным контактом:
class Button
{
boost::smart_ptr<InputPin> p_input_pin;
}
Или изменил класс контактов, чтобы у него был список подписчиков . Это может быть лучший дизайн, поскольку он позволяет многим подписчикам получать уведомления об изменении значения пина. Вы также можете добавить метод (установщик), который устанавливает значение пина.
Вариант использования 1: Нажатие кнопки.
Нажатие кнопки изменяет состояние входного вывода.
Входной контакт уведомляет подписчиков о событии.
Вариант использования 2: прерывание.
Прерывание {объект} изменяет состояние входного вывода.
Входной контакт уведомляет подписчиков о событии.
Шаблон проектирования «Подписчик / Издатель» хорошо подходит для этих случаев использования.
В случае, если это поможет кому-то еще, я нашел способ обойти это:
typedef Button< Pin<B7>, &OnButtonChange > TestButton;
PinIRQ< B7, &TestButton::InputChangedISR > irqPin;
TestButton testButton;
Объекту Button на самом деле не нужно ничего знать о Прерывание PinIRQ, поэтому я могу просто объявить его, используя исходный (без прерывания) класс Pin, которому не требуется ничего, связанного с передачей Button в качестве параметра шаблона. Я могу создать экземпляр полного класса PinIRQ с помощью этого объявления Button, и все работает отлично.
Я получаю PinIRQ из объекта Pin, чтобы позволить мне «перегрузить» шаблон класса, чтобы получить Pin
и PinIRQ
Это не самый приятный фрагмент кода, который я когда-либо писал, но он, по крайней мере, работает.
Я думаю, это может быть случай преждевременной оптимизации?
Как вы думаете, почему вам нужно выполнить компиляцию до прямого кода, а не отправлять во время выполнения?
Может быть, посмотрите на Любопытно повторяющийся шаблон шаблона
Что, если бы вы начали с этого
template <int pinNumber> class InputPin
{
static bool IsHigh()
{
return ( (*portAddress) & (1<<pinNumber) );
}
};
template <int Pin> class Button
{
static bool IsPressed()
{
return !InputPin<Pin>::IsHigh(); // might need typename here
}
};
Button < 1 > powerButton;
if (powerButton.IsPressed())
Возможно, тогда он мог бы развиться до
template <int pinNumber, typename event> class InputPin
{
static bool IsHigh()
{
event::OnA();
return ( (*portAddress) & (1<<pinNumber) );
}
};
template <int Pin> class Button
{
static bool IsPressed()
{
return !InputPin<Pin,Button>::IsHigh(); // might need typename here
}
OnA(){}
OnB(){}
};