Object-oriented programming (OOP) is a programming paradigm used across programming languages, development environments, and industries. In this article, we will discuss frequently asked questions on LabVIEW OOP. Please note that this document assumes a basic understanding of OOP terminology and processes.
C++ is a text-based, functional language. LabVIEW is a graphical, dataflow language. The different paradigms require differences in how they present OOP features:
For more explanation of these difference, please refer to LabVIEW Object-Oriented Programming: the Decisions Behind the Design.
What is the benefit to a brand new user of LabVIEW?
What is the benefit to an experienced G programmer who does not practice OOP?
For a test and measurement example that contrasts a task-based approach and an object-oriented approach, refer to the Board Testing example that ships with LabVIEW 8.2 and later.
LabVIEW 8.2 and later ships with several LabVIEW object-oriented programming examples. Refer also to the Additional Resources section of this document.
The simplest start is to convert existing type definitions to LabVIEW classes. You can do this by right-clicking a typedef in the Project Explorer window and selecting Convert Contents to Class from the shortcut menu. See the LabVIEW Help for more information about creating LabVIEW classes.
Note: LabVIEW does not preserve the default value of controls and constants during the conversion process. To access the details of the typedefs, add VIs to those classes that need to access to the type definition. This grouping of VIs based on the data they act on is fundamental to object-oriented programming. Refer to the two NI Week 2006 presentations listed below in the Additional Information section for more information about object-oriented design.
Refer to the Reference Object example that ships with LabVIEW 8.2 and later.
Essentially, you make the private data of a class be a reference. The example shows how to use a single-element queue reference for this purpose. You may also use the GOOP Toolkit (refer to the LabVIEW Object-Oriented Programming & GOOP Toolkit section below).
With LabVIEW 2009, object orientation is now supported on LabVIEW real-time targets. Object-oriented programming is not supported in real-time with LabVIEW 8.6 and prior.
When you close a VI, LabVIEW knows all the subVIs that will be unused as a result of closing that VI, so LabVIEW can ask to save them all at once. Classes, however, cannot leave memory until all data instances have left memory, and LabVIEW cannot know whether closing any given VI will actually close out the last instance of data. A VI might not have any reference to the class in its code, but it might still have an instance of the class stored in a variant somewhere. If all VIs were idle, LabVIEW could determine how many instances of the class will leave with this set of VIs and see if that accounts for all the instances of the class remaining in memory. Then LabVIEW would know that the class is leaving memory with the VIs and could ask about the class in the same Save Changes dialog box as the other VIs/libraries. But if any VI is still running, the number of instances can be constantly fluctuating. And while VIs are running, data can be hiding in impossible-to-search locations, such as a queue or notifier, and these extra spaces only get disposed when the VI goes idle. A running VI also can create new instances in locations that LabVIEW already checked. So it is impossible to count instances while the VIs are running. Since LabVIEW does not abort running VIs until after asking whether or not to save the changes, LabVIEW cannot yet know if the class will be leaving memory. Even if all the VIs were idle and LabVIEW could do the search for data, that search would be extremely slow, so LabVIEW does not attempt it at all. Thus, when you close a VI, the classes frequently end up getting their own Save Changes dialog box.
The only time LabVIEW can fold classes in with everything else and offer a single Save Changes dialog box is when you are closing the project. In that case, because every VI in the project will be closed down, LabVIEW know that means all the data instances of the class will be disposed without having to account for them all.
You can find a free download of the GOOP Development Suite on the NI website.
Most users will find the LabVIEW classes work best for their applications. LabVIEW classes behave just like the clusters that users are familiar with.
You can use references to handle communication between sections of code that execute in parallel. For customers who are actually modeling system resources, the reference model becomes very useful and they may wish to create GOOP classes. References are also valuable when creating graph and tree data structures. Even in these cases, the data fields at the core of the GOOP class may well be LabVIEW classes. However, when using references or any other means of communicating between sections of parallel code, you need to know about locking, race conditions, and other complexities that arise.
You can use the reference model to guarantee that you never duplicate that class in memory. For a class that requires a lot of memory, this can be an advantage.
There is no requirement that you rewrite existing GOOP classes as LabVIEW classes. One reason is that you should never destroy working code without a compelling reason, and the GOOP classes will continue to work in LabVIEW 8.2 and beyond. After all, the GOOP Toolkit is written in LabVIEW. Further, changing code that works by-reference into code that works by-value is a significant undertaking.
One reason to transition is performance. A lot of overhead is generated for an object when every function call has to look up the reference, lock the instance, and extract the data. The GOOP Toolkit does an amazing job of minimizing this overhead, but it is there nonetheless. Further, operating using a refnum prevents many operations that could otherwise proceed in parallel. If you have a class that does not need the by-reference behavior and does need better performance, then you could consider transitioning the GOOP class to the LabVIEW class.
This is a question that must be answered on a case-by-case basis. LabVIEW has no automated way to make the conversion.
Because of the consistency with dataflow and the native compiler implementation of dynamic dispatching, LabVIEW classes will tend to outperform GOOP classes in equivalent situations. That having been said, there are very few equivalent situations. The LabVIEW classes are designed to replace clusters and to enhance dataflow. GOOP classes are a way to add references to LabVIEW. They serve two different purposes in the language. When creating new classes, you should make the decision between a GOOP class or a LabVIEW class based on the functionality the class needs, not based on performance predictions.
Benchmarking on a range of computers shows the performance between these two is nearly identical. Refer to the question below for more performance information.
A dynamic dispatching subVI has some small overhead when LabVIEW looks up which subVI to invoke. The overhead is constant. It does not increase as you add more classes or as you add more dynamic VIs to a class. There can be a performance hit if the subVI call has to make extra copies of the parameters to match the functionality of the actual subVI LabVIEW invokes. You can avoid this by wiring the input front panel terminals to output front panel terminals even on dynamic VIs that you never expect to call (for example, parent VIs that exist only to define the connector pane for children to override). Refer to LabVIEW Object-Oriented Programming: the Decisions Behind the Design for details on how the dynamic dispatching is able to achieve constant overhead performance.
A LabVIEW class is a type of project library. It is a project library dedicated to the task of defining a data type. Like a project library, the LabVIEW class defines scope and namespace for its member VIs, and a VI can be owned by one and only one LabVIEW class.
The LabVIEW class has a private data control. This is a control VI that defines the data values of the class. This special control is not part of other library types, and is actually saved inside the class file. LabVIEW classes have all the properties that libraries have, and some additional properties. You can see the additional properties in the Class Properties dialog box on the Inheritance, Probes and Wire Appearance pages.
The only ability that project libraries have that LabVIEW classes do not is the ability to contain sublibraries.
If a VI uses a LabVIEW class, then loading the VI loads the class, just like a VI loads all of its subVIs. A LabVIEW class in turn loads all of its member VIs and its parent class. Parent classes do not automatically trigger the loading of their child classes. Parents do not have any links to children. A member VI of a parent may use a child class and thus LabVIEW loads the child class.
"By-value" means that the wire carries actual data of the type of the wire. "By-reference" means that the wire carries a reference to the data of the type, and LabVIEW stores the actual data in some central repository. Most data types in LabVIEW, including numerics, arrays, clusters, strings, Booleans, and paths are by value. Typically, the only types that are by reference are those that use communications or reflect system resources, such as file refnums, queue refnums, and VI Server refnums. LabVIEW classes are by value. In by-value syntax, each object has its own data values, which you can modify independently of other objects. In by-reference syntax, there is a single instance of data to which multiple objects refer. If one object modifies the data, it has to be careful not to conflict with other objects who are reading and writing that data. The primary pros and cons are:
Refer to LabVIEW Object-Oriented Programming: the Decisions Behind the Design.
NI does not have a native UML tool. Endevo makes a tool for generating classes from UML diagrams, and generating UML diagrams from code. The tool currently works for GOOP classes (refer to the LabVIEW Object-Oriented & GOOP Toolkit section above), but an upcoming release will support LabVIEW classes as well.
Make Current Value Default is a valuable feature of LabVIEW. You might use it frequently for building test VIs. This feature is not supported for LabVIEW classes in LabVIEW 8.2 and later because of the complexity of providing a good user interface so that the user knows when the data is something other than the default-default, even though the user cannot actually see the data because the data is private. This feature is a high priority to be included in a future LabVIEW version.
Reentrant dynamic VIs are not supported feature in LabVIEW 8.2. Because dynamic subVI nodes at edit time do not know which subVI LabVIEW will actually invoke at run time, each node would have to create clones of all the possible subVIs. This turns out to be prohibitively expensive in memory.
(LabVIEW 8.5) You can use reentrant dynamic VIs in LabVIEW 8.5.
You can configure a dynamic dispatch VI to be recursive, or to use itself as part of its own definition. Recursive VIs can call themselves on their own block diagram, including the block diagram of subVIs. Recursion is useful if you want to operate many times on the output of the same process. You can configure a dynamic dispatch member VI to allow recursion by setting it to be reentrant and to share clones between instances.
Complete the following steps to configure a VI for recursion.
Note: Not accounting for infinite recursion causes unnecessary memory usage and might crash LabVIEW. To avoid infinite recursion, consider calling the recursive VI in a Case structure.