I have been developing control software in C++. My hardware consists of a microcontroller with an integrated a/d converter and an external on board a/d converter. Both of these a/d converters have different features but there are some commonalities (both of them are capable to offer value of given analog input).
// analog inputs enum class Input { Channel_00, Channel_01, Channel_02, Channel_04, Channel_05, Channel_06, Channel_07 }; // driver of the internal a/d converter class AdcInt { public: float getValue(Input input); }; // driver of the external a/d converter class AdcExt { public: float getValue(Input input); };
Based on the drivers for the a/d converters I am going to start building the application layer. One of the building stones of my design is the AnalogInput
class. This class is intended to exploit the drivers and offers some additional services e.g. conversion the raw value into the physical units.
class AnalogInput { public: float getConvertedValue(); }
I have been thinking about how to ensure that the AnalogInput
class can operate with both the driver objects (AdcInt
, AdcExt
) which is necessary because the analog inputs can be connected to any of the a/d converters and I need to work with all the analog inputs in uniform manner.
- First idea
My first idea how to do that is based on common interface for all the a/d converter drivers let's say AdcDriver
and defining the virtual
getValue
method as a part of this interface. The AnalogInput
class would then receive pointer to the AdcDriver
interface in its constructor. The drawback of this idea is that the getValue
method is virtual which is unsuitable for usage in the interrupt service routine (which is my requirement).
- Possible solution
Due to the drawback of my first idea I have started to look for another approach how to create common interface AdcDriver
without virtual method. I have found the so called curiously recurring template pattern (CRTP).
template<class T> class AdcDriver { public: float getValue(Input input) { return static_cast<T*>(this)->getValue(input); } } class AdcInt : public AdcDriver<AdcInt> { public: float getValue(Input input) { // ... implementation specific for AdcInt } } class AdcExt : public AdcDriver<AdcExt> { public: float getValue(Input input) { // ... implementation specific for AdcExt } } template<class T> class AnalogInput { public: AnalogInput(T& _driver, Input _input_id) : driver(_driver) { input_id = _input_id; } float getConvertedValue() { return convert(driver.getValue()); } private: T& driver; Input input_id; float convert(float); } int main(int argc, char** argv) { AdcInt internal_adc; AdcExt external_adc; AnalogInput<AdcInt> analog_input_01(internal_adc, Input::Channel_01); AnalogInput<AdcExt> analog_input_02(external_adc, Input::Channel_02); analog_input_01.getConvertedValue(); analog_input_02.getConvertedValue(); return 0; }
Do you think that the second approach which I have described above is appropriate solution of my problem? If you don't think so can you recommend me better idea?
getValue()
method? 2) Why do you think calling a virtual method is not allowed in an interrupt? It is just an indirect function call.AdcDriver
class twice - once for each A/D device. Then at run time you instantiate the correct one only and hook it up to your interrupt vector. So I take back my remark about CRTP above.