类模板实例化:有什么办法绕过这个循环引用吗?

发布于 2024-09-05 09:29:57 字数 1133 浏览 4 评论 0原文

我有两个类用来表示某些硬件:一个 Button 和一个 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类多了一个静态成员函数,当引脚值改变时,硬件会自动调用该函数。我希望它能够通知 Button 类,以便 Button 类可以通知主应用程序它已被按下/释放。我目前正在通过将指针传递给回调函数来做到这一点。为了让编译器内联回调代码,我认为需要将这些函数指针作为模板参数传递。所以现在,这两个新类都有一个额外的模板参数,它是指向回调函数的指针。不幸的是,这给了我一个循环引用,因为要实例化 ButtonIRQ 类,我现在必须执行如下操作:

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

其中 ...... 代表循环引用。

有谁知道如何避免这种循环引用?我是模板新手,所以可能缺少一些非常简单的东西。

附注重要的是,编译器确切地知道中断发生时将运行哪些代码,因为它然后会进行一些非常有用的优化 - 它能够内联回调函数并在 ah/ 上调用的确切地址处逐字插入回调函数的代码w 中断。

I have two classes that I'm using to represent some hardware: A Button and an InputPin class which represent a button that will change the value of an IC's input pin when it's pressed down. A simple example of them is:

template <int pinNumber> class InputPin 
{
  static bool IsHigh()
  {
     return ( (*portAddress) & (1<<pinNumber) );
  }
};

template <typename InputPin> class Button 
{
  static bool IsPressed()
  {
    return !InputPin::IsHigh();
  }
};

This works beautifully and by using class templates, the condition below will compile as tightly as if I'd handwritten it in assembly (a single instruction).

Button < InputPin<1> > powerButton;

if (powerButton.IsPressed())
   ........;

However, I am extending it to deal with interrupts and have got a problem with circular references.

Compared to the original InputPin, a new InputPinIRQ class has an extra static member function that will be called automatically by the hardware when the pin value changes. I'd like it to be able to notify the Button class of this, so that the Button class can then notify the main application that it has been pressed/released. I am currently doing this by passing pointers to callback functions. In order for the callback code to be inlined by the compiler, I believe that need to pass these function pointers as template parameters. So now, both of the new classes have an extra template parameter that is a pointer to a callback function. Unfortunately this gives me a circular reference because to instantiate a ButtonIRQ class I now have to do something like this:

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

where the ...... represents the circular reference.

Does anyone know how I can avoid this circular reference? I am new to templates, so am probably missing something really simple.

ps. it's important that the compiler knows exactly what code will be run when the interrupt occurs as it then does some very useful optimisation - it is able to inline the callback function and literally inserts the callback function's code at the exact address that is called on a h/w interrupt.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

巴黎夜雨 2024-09-12 09:29:57

我会更改设计,以便按钮与输入引脚相关联:

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

或者更改引脚类别,使其具有订阅者列表。这可能是更好的设计,因为它允许在 pin 值更改时通知许多订阅者。您还可以添加一个方法(设置器)来设置引脚的值。

用例 1:按下按钮。
按下按钮会改变输入引脚的状态。
输入引脚通知事件的订阅者。

用例 2:中断。
中断{object}更改输入引脚的状态。
输入引脚通知事件的订阅者。

订阅者/发布者设计模式非常适合这些用例。

I would change the design so that a button is associated with an input pin:

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

Or change the pin class so that it has a list of subscribers. This may be the better design since it allows many subscribers to be notified when the pin has changed its value. You could also add a method (setter) that sets the pin's value.

Use case 1: Button press.
Button Press changes state of input pin.
Input pin notifies subscribers of event.

Use case 2: Interrupt.
Interrupt {object} changes state of input pin.
Input pin notifies subscribers of event.

The Subscriber / Publisher design pattern suits these use cases well.

冰之心 2024-09-12 09:29:57

如果它对其他人有帮助,我已经解决了这个问题:

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

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

Button 对象实际上不需要了解有关 PinIRQ 中断的任何信息,因此我可以使用原始(无中断)Pin 类来声明它,它不需要任何与按钮相关的传递作为模板参数。我可以使用此 Button 声明实例化完整的 PinIRQ 类,并且一切正常。

我从 Pin 对象派生 PinIRQ,以便允许我“重载”类模板以提供 PinPinIRQ

这不是我写过的最好的代码,但它至少可以工作。

In case it will help anyone else, I have worked out a way round this:

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

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

The Button object doesn't actually need to know anything about the PinIRQ's interrupt, so I can just declare it using the original (interrupt-less) Pin class, which needs nothing Button related passing as a template parameter. The I can instantiate a full PinIRQ class using this Button declaration and everything works perfectly.

I'm deriving the PinIRQ from the Pin object in order to allow me to "overload" the class template to give me Pin<int PinNumber> and PinIRQ<int PinNumber, void (*ISR)()>

Its not the nicest piece of code I've ever written, but it works at least.

蝶…霜飞 2024-09-12 09:29:57

我认为这可能是过早优化的情况?

为什么您认为需要将其编译为直接代码,而不是在运行时调度?

I think this might be a case of premature optimisation?

Why do you think you need to have the compile down to straight code, rather than dispatching at runtime?

慵挽 2024-09-12 09:29:57

也许看看奇怪的重复模板模式

如果你从这个开始的话

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(){}
};

Maybe look at the Curiously Recurring Template Pattern

What if you started from this

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())

Perhaps then it might progress to

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(){}
};
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文