Today’s entry was inspired by a question I recently received from someone while presenting at Developer Days in Salt Lake City – I was asked, “when and why should I use the ‘preserve run-time class’ node, and what’s the difference between it and the ‘to more specific’ primitive?” It was a good question, and something that I didn’t fully understand myself until I spent some serious time doing object-oriented programming. What follows is my attempt to explain this for anyone else who might have ever wondered the same thing.
The ‘to more specific’ node is a mechanism to tell the LabVIEW compiler that an object is guaranteed to belong to a certain class of objects. Without this assurance, the compiler has no way of knowing what the object belongs to or is capable of at compile time, and would therefore show a broken wire if you attempted to pass the object wire into a function that can only act upon a certain class of objects.
Perhaps the most common illustration of this is a plug-in framework using classes (also known as a factory pattern). A factory pattern enables you to add or change functionality simply by loading a new child class at run-time. It is required that the calling application already have in memory a common parent, but the newly loaded child class can then override or extend the behavior of dynamically dispatched methods. As an example, a framework may already be written to act upon a generic measurement, which has methods to initialize, acquire and analyze a signal. A specific measurement, such as a frequency sweep, could redefine the implementation of these methods.
Shown below is the code required to load a specific measurement at run-time. The ‘Get LV Class Default Value.vi’ brings a class into memory from a location on disk, but the compiler has no way of knowing the type of object prior to this operation. This is why we use the ‘to more specific class’ node, which then allows us to treat the object as belonging to a specific hierarchy – in this case, we treat it as a measurement. Note that the error handling is designed such that we only add the loaded class to the array if it is a valid member of that class hierarchy. If we had not handled this correctly, and we attempted to load an invalid class, this node would return the parent ‘Measurement.lvclass’ object.
The ‘preserve run-time class’ node is also a way to give the LabVIEW compiler a guarantee that it would not otherwise have at compile-time – in this case, we are telling the compiler exactly what class of object will be passed out. The primary use-case for this node is within a VI that has a dynamic input terminal and a dynamic output terminal for the same class, but there is not a guarantee that the object’s class will be preserved between them. If, for example, we are dynamically loading an object that will replace the object that was passed into the VI, the compiler has no way to know what class the object will be, and will therefore have a broken arrow. ‘To more specific’ cannot be used in this case, as we need to guarantee the exact class of the object.
In the example below, the other case of the case structure is set to ‘Use Default’ for the class wire, which means that LabVIEW does not know at compile-time what the class of object is that it will receive. The first diagram of ‘common.vi’ attempts to use ‘to more specific’ to indicate that the object will be a child of ‘Hardware.lvclass,’ but it still does not guarantee that it will be ‘SCOPE.lvclass.’ As a result, the calling VI, initialize.vi, which has dynamic input and output terminals, is broken, as it needs to know that the exact same class of object will be passed between.
The diagram below shows how we use ‘preserve run-time class’ to guarantee that the class of the object will be the same as it receives. As a result, initialize.vi can compile and we no longer see a broken run arrow.
This illustrates the most common and important use case for ‘preserve run-time class.’
As always, you can find more information on these in the LabVIEW help, but free to post questions and comments!