我有两个类用来代表一些硬件:一个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;
其中......代表循环引用。
有谁知道如何避免这种循环引用?我是模板的新手,所以我可能错过了一些非常简单的东西。
PS。重要的是编译器确切地知道在中断发生时将运行什么代码,因为它然后执行一些非常有用的优化 - 它能够内联回调函数并且在回调函数的代码上插入调用的确切地址啊/ w中断。
答案 0 :(得分:1)
我会更改设计,以便按钮与输入引脚相关联:
class Button
{
boost::smart_ptr<InputPin> p_input_pin;
}
或者更改pin类,使其具有订阅者列表。这可能是更好的设计,因为它允许许多订户在引脚更改其值时得到通知。您还可以添加一个设置引脚值的方法(setter)。
使用案例1:按下按钮。
按钮按下更改输入引脚的状态
输入引脚通知订户事件。
用例2:中断。
中断{object}改变输入引脚的状态
输入引脚通知订户事件。
Subscriber / Publisher设计模式很适合这些用例。
答案 1 :(得分:1)
如果它会帮助其他任何人,我已经找到了解决方法:
typedef Button< Pin<B7>, &OnButtonChange > TestButton;
PinIRQ< B7, &TestButton::InputChangedISR > irqPin;
TestButton testButton;
Button对象实际上并不需要知道关于PinIRQ中断的任何信息,所以我可以使用原始(无中断)Pin类来声明它,它不需要任何与Button相关的传递作为模板参数。我可以使用这个Button声明实例化一个完整的PinIRQ类,一切都很完美。
我从Pin对象派生PinIRQ,以便让我“重载”类模板给我Pin<int PinNumber>
和PinIRQ<int PinNumber, void (*ISR)()>
它不是我写过的最好的一段代码,但它起码至少。
答案 2 :(得分:0)
我认为这可能是过早优化的情况?
为什么您认为需要将编译简化为直接代码,而不是在运行时调度?
答案 3 :(得分:0)
也许看看Curiously Recurring Template Pattern
如果你从这个
开始怎么办?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(){}
};