Note that there are some explanatory texts on larger screens.

plurals
  1. POSimplify config-file-driven factory
    text
    copied!<p>Hej!</p> <p>Caution this is a long post -- if you are easily annoyed better skip it. ;-)</p> <p>The Project I am working on currently is about reading voltage measurements of different sensors and calculating the respective physical value. There are a number of channels each of which can have a different sensor attached. Basicly the kind of sensor is known but the details like sensitivity and bias can vary widely. While there is a parametrized class for each type of sensor, the parameters are pulled from config files.</p> <p>Suppose there is the following class hierachy:</p> <pre><code>class Sensor { public: virtual double Calculate(const double &amp;arg)=0; }; class PTCResistor { PTCResistor(XMLNode &amp;node); double Calculate(const double &amp;arg); }; class Thermocouple { Thermocouple(XMLNode &amp;node); double Calculate(const double &amp;arg); }; </code></pre> <p>The main config file looks like this:</p> <pre><code>&lt;channels&gt; &lt;TurbineInletTemp sensor="Thermocouple.TypeK.xml" /&gt; &lt;CylinderHeadTemp sensor="PTCResistor.PT500.xml" /&gt; ... &lt;/channels&gt; </code></pre> <p>As you see each channel tag has a attribute sensor specifying some ohter XML-File which contians parameter information and the kind of sensor. It looks something like this:</p> <pre><code>&lt;sensor class="PTCResistor"&gt; &lt;param Rref="500" Tref="0"&gt; ... &lt;/sensor&gt; </code></pre> <p>The format of these reference XML-files can vary but is is understood by the constructor of each derived class.</p> <p>Upon program start the main config file is parsed and a factory class resolves the link to the XMLs containing the sensor specific information, examines the class tag and calls the respective constructors. Like this:</p> <pre><code>string chnTITFile = rootNode.GetNode("TurbineInletTemp").GetAttribute("sensor"); XMLNode chnTITNode = XMLNode.parseFile(RootPath + chnTITFile,"sensor") string className = chnTITNode .GetAttribute("class"); if(className == "Thermocouple") { Sensor* sensorTIT = new Thermocouple(chnTITNode); } else if(className = "PTCResistor") { Sensor* sensorTIT = new PTCResistor(chnTITNode); } </code></pre> <p>This is the leanest solution I came up with in the sense that there are only few places to alter when a new derived class is added. Basicly one writes a new derived class and the adds another if-branch in the factory. Unfortunately this involves a lot of string comparisons. Since the number of classes and channels can be rather large (and the target system is low-powered) I am concerned. </p> <p>Another possibility would be hash the class names. This would probably look like this:</p> <pre><code>string chnTITFile = rootNode.GetNode("TurbineInletTemp).GetAttribute("sensor"); XMLNode chnTITNode = XMLNode.parseFile(RootPath + chnTITFile,"sensor") string className = chnTITNode .GetAttribute("class"); switch(hash(className)) { case ThermocoupleHash: ... case PTCResistorHash: ... ... } </code></pre> <p>Problem here: need to maintain the enum containing the hash-values. </p> <p>As I am writing this code I feel that the process of adding a new sensor can be rather tedious. Any ideas how to reduce the mess? Or is this already the least of evil one can possibly get?</p> <p>Thanks for reading! Arne</p> <p>EDIT1</p> <p>As suggested by Neil Butterworth I consider to extend each class by a function like</p> <pre><code> static Sensor* Thermocouple::Create(XMLNode &amp;node) { return new Thermocouple(node); } </code></pre> <p>and create a hash table that relates the class name string to a function pointer to this static function:</p> <pre><code>typedef Sensor* (*CreateFunct)(XMLNode &amp;node); class SensorFactory { public: SensorFactory() { classNameMap["Thermocouple"] = Thermocouple::Create; classNameMap["PTCResistor"] = PTCResistor::Create; }; Sensor* ChannelByName(string chnName) { string chnFile= rootNode.GetNode(chnName).GetAttribute("sensor"); XMLNode chnSensorNode = XMLNode.parseFile(RootPath + chnFile,"sensor") string className = chnSensorNode.GetAttribute("class"); map&lt;string, CreateFunct&gt;::iterator iterat = classNameMap.find(className); if(iterat != classNameMap.end()) { CreateFunct f = iterat-&gt;second; return f(chnTITNode); } }; private: map&lt;string, CreateFunct&gt; classNameMap; } </code></pre> <p>This allows to create the matching sensor object for a channel identified by name. The lookup is accomplished by hash-comparision and one only needs to maintain map initialization in the constructor of the factory class.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload