Создание шаблона класса: как-нибудь обойти эту круговую ссылку? - PullRequest
1 голос
/ 15 июня 2010

У меня есть два класса, которые я использую для представления некоторого оборудования: класс Button и класс InputPin, которые представляют собой кнопку, которая будет изменять значение входного вывода микросхемы при его нажатии.Простой пример из них:

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 имеет дополнительную статическую функцию-член, которая будет автоматически вызываться аппаратным обеспечением при изменении значения контакта.Я хотел бы, чтобы он мог уведомить класс Button об этом, чтобы класс Button мог затем уведомить основное приложение о том, что оно было нажато / отпущено.В настоящее время я делаю это, передавая указатели на функции обратного вызова.Чтобы код обратного вызова был встроен компилятором, я считаю, что необходимо передать эти указатели функций в качестве параметров шаблона.Итак, теперь у обоих новых классов есть дополнительный параметр шаблона, который является указателем на функцию обратного вызова.К сожалению, это дает мне циклическую ссылку, потому что для создания экземпляра класса ButtonIRQ мне теперь нужно сделать что-то вроде этого:

ButtonIRQ<InputPinIRQ<1, ButtonIRQ< Inp.... >::OnPinChange>, OnButtonChange> pB;

, где ...... представляет циклическую ссылку.

Кто-нибудь знает, как мне избежать этой циркулярной ссылки?Я новичок в шаблонах, поэтому, возможно, мне не хватает чего-то очень простого.

пс.важно, чтобы компилятор точно знал, какой код будет запущен при возникновении прерывания, поскольку он затем выполняет очень полезную оптимизацию - он может встроить функцию обратного вызова и буквально вставить код функции обратного вызова по точному адресу, который вызывается через ah /ш прерывание.

Ответы [ 4 ]

1 голос
/ 17 июня 2010

На случай, если это кому-нибудь еще поможет, я обошёл это:

typedef Button< Pin<B7>,  &OnButtonChange > TestButton;

PinIRQ< B7, &TestButton::InputChangedISR > irqPin; 
TestButton testButton;

Объекту Button на самом деле не нужно ничего знать о прерывании PinIRQ, поэтому я могу просто объявить его, используя оригинальный класс Pin (без прерываний), который ничего не требует для передачи, связанной с Button, как параметр шаблона. Я могу создать полный класс PinIRQ, используя это объявление Button, и все работает отлично.

Я извлекаю PinIRQ из объекта Pin, чтобы позволить мне «перегрузить» шаблон класса, чтобы получить Pin<int PinNumber> и PinIRQ<int PinNumber, void (*ISR)()>

Это не самый хороший кусок кода, который я когда-либо писал, но он работает по крайней мере.

1 голос
/ 15 июня 2010

Я бы изменил дизайн так, чтобы кнопка ассоциировалась с входным контактом:

class Button
{
  boost::smart_ptr<InputPin> p_input_pin;
}

Или измените класс пин-кода, чтобы у него был список подписчиков . Это может быть лучшим дизайном, поскольку позволяет многим подписчикам получать уведомления, когда пин-код изменил свое значение. Вы также можете добавить метод (установщик), который устанавливает значение булавки.

Вариант использования 1: нажатие кнопки.
Нажатие кнопки изменяет состояние входного контакта.
Входной пин уведомляет подписчиков о событии.

Вариант использования 2: Прерывание.
Прерывание {объект} изменяет состояние входного контакта.
Входной пин уведомляет подписчиков о событии.

Шаблон проектирования подписчика / издателя хорошо подходит для этих случаев использования.

0 голосов
/ 16 июня 2010

Может быть, посмотрите на Любопытно повторяющийся шаблон

Что если вы начали с этого

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(){}
};
0 голосов
/ 16 июня 2010

Я думаю, что это может быть причиной преждевременной оптимизации?

Почему, по вашему мнению, вам нужна компиляция до прямого кода, а не диспетчеризация во время выполнения?

...