A variant is a data type that stores both a value and metadata specifying how the data should be interpreted. Variants are not variables without a data type but typed variables that can change their data type as needed. Common in Microsoft Visual Basic and available in C++, variants were introduced in LabVIEW 5 to handle values passed to and from ActiveX objects on the Windows platform. LabVIEW 6i, introduces variants across all platforms as native LabVIEW data types. (Learn about the latest version of LabVIEW.)
First, variants provide a way of passing values between programs written using different compilers that might not share the same data types. Because metadata is passed along with a value, the interface layer can provide translation services between the two programs. For this to be successful, variants must follow a common format; Microsoft COM specification defines this format. Many of the ActiveX components that you can incorporate into a LabVIEW program uses variants in their properties and methods. This allows LabVIEW to manipulate variables without having native support for the data type.
Second, variant types allow software to achieve a greater degree of generality. Variants allow a program to manipulate data without specifying what kind of data it is at compile time. Variants are not the only way to obtain generality; a pointer passed to a C function can point to almost anything. But variants provide a safer way of treating generalized information than pointer-passing because LabVIEW passes metadata with the value.
Visual Basic makes extensive (perhaps excessive) use of variants; variants are the default data type, and some Visual Basic features such as collections only work with variants. In addition to the usual scalar and array data types, Visual Basic variants can refer to objects. While it sometimes seems as though Visual Basic variants exist solely to facilitate sloppy code, storing object references in variants is genuinely useful, and many object manipulation algorithms would be difficult to write otherwise.
LabVIEW is a much more strictly-typed language than Visual Basic. LabVIEW's polymorphic functions and automatic coercion try to fit a function to its inputs, but if they cannot, the result is a broken wire as shown in Figure 1b.
Figure 1: Variants and Type Checking in LabVIEW
Type checking in LabVIEW makes it virtually impossible to call a function with incorrect arguments, eliminating an entire category of potential bugs. The downside of strict type checking is that it sometimes gets in the way of what you are trying to accomplish.
The Flatten to String function is the traditional way of overcoming strict type checking in LabVIEW. For example, the queue VIs only accept strings. You must flatten other data types to strings before you can pass them into a queue. However, flattened strings contain no information about how to interpret the data they contain. Unflattening the strings with the wrong data type results in a runtime error as shown in Figure 2a.
Figure 2: Overcoming Strict Type Checking in LabVIEW
The LabVIEW variant, by contrast, knows its own data type. This allows a variant to be converted to any specific data type on demand, as shown in Figure 2b. This provides protection from runtime errors, although they can still occur if LabVIEW cannot perform the conversion. When converting variant data to or from LabVIEW data, you should avoid performing further operations on a wire branch of the original source data. If you continue to use the variant data after the initial conversion, LabVIEW must make a copy of the data, which can slow performance.
If a LabVIEW variant knows its own data type, why must you specify a data type in the Variant to Data function? Why can the variant not just unflatten itself? This is by design rather than by necessity. Variants must fit within the existing LabVIEW paradigm, namely that the data type of every wire is defined at design time. If a variant unflattened itself, what color would the wire be? While it is understandable that LabVIEW does not allow a variant to unflatten itself, LabVIEW needs a function that returns the data type of the variant as an enumerated type, similar to the VarType function in Visual Basic. Without any way to check data types before attempting a conversion, the only recourse is to guess an acceptable data type and then check for an error when the conversion fails.
Another implementation limitation is that the mathematical functions in LabVIEW do not operate on variants, presumably because the mathematical operators have no mechanism for passing out an error at runtime when conversion fails. This is more restrictive than other languages; in Visual Basic, it is acceptable to add, 5.00 (internally a DBL) to 5 (internally an I32). Because strictly-typed code executes faster, uses less memory, and is less error-prone than variant-using code, it is hard to make the case that this feature is essential. But the fact remains that the behavior of LabVIEW is different from other languages.
The comparison operators in LabVIEW also behave differently than you might expect. When making comparisons, Visual Basic tries to coerce numeric values to the same data type before making the comparison. So in Visual Basic, 22.00 (internally a DBL) is equal to 22 (internally an I32). In LabVIEW, these two variants are not equal. 5 (internally a U32) is not equal to the variant 5 (internally an I32), even though the byte representation of these two numbers is exactly the same. This is covered in more detail in the following sections.
Finally, Visual Basic variants have three special data types (empty, null, and error). The null type arises naturally when working with databases (fields in a record are allowed to have null values), so Database Connectivity Toolkit has a special Database Variant to Data function that handles null values, but detecting them requires several steps. Built-in support for null variants would be useful.
LabVIEW variants have a special feature of their own: the ability to associate named attributes with a variant. An attribute is a key-value pair that LabVIEW can store in and recall from a variant. A LabVIEW variant can store zero or more attributes, and the values of each attribute are themselves variants. The waveform data type also features attributes, so it is possible that attributes exist in variants in order to support their use in the waveform VIs.
It is difficult to see how variant attributes fit into the LabVIEW design philosophy, which is based on simple data types and strict type checking. They satisfy a need for bundling auxiliary data that, for most users, would be better met with a custom type-defined cluster. Variant attributes essentially provide associative array functionality. While associative arrays are a valuable tool in building algorithms such as linked lists and trees in languages without pointers, that is not the main reason LabVIEW developers added variants to the G language.
When is the variant data type appropriate? The following list includes some circumstances when variants are either required, or enable a cleaner solution than other methods.
- Use variant types when working with ActiveX objects that require them. There's generally no reason not to convert such variants into a specific LabVIEW type immediately, and doing this conversion immediately can have substantial performance advantages when a large number of variants are in use.
- Use variants in conjunction with LabVIEW control references to read or modify values of a programmatically-selected control. Control references are a great feature of LabVIEW that provide significant functionality to programmers. When a control reference is not strictly typed as shown in Figure 3, a variant is required for the value property. Keep in mind that modifying control values by reference is much slower than wiring directly to the indicator or its local variables.
Figure 3: When a Control Reference Is Not Strictly Typed
- Use variants in library routines when their use makes the package more flexible and reusable. A good library routine (such as a hash function or binary tree) takes a lot of work to write, and once written it should ideally be usable in a variety of ways.
Consider using the following tips when working with variants:
- Do not use variants in place of polymorphic VIs in situations where data types can be determined at design time. Polymorphic VIs yield cleaner, faster code and require less runtime error checking. They take longer to write because one VI must be written for each supported type, but are easier to use once written.
- Do not use variant controls on VIs that will be seen by the end user. Not only do variant controls look bad and take up a lot of room, in certain cases, they can cause significant performance problems. The performance issue appears to be limited to variants created by ActiveX objects.
- Do not use variant attributes in place of a cluster, or to store large numbers of values. Variant attributes provide a quick and dirty associative array functionality, but attribute lookups are O(n) operations (time required for the operation increases in proportion to the array size). Using a variant this way also does not match the usual LabVIEW conventions.
- Avoid using comparison operators on variants, when possible. LabVIEW supports comparison of variants, but the comparison rules are not always what one might expect. Two variants are equal when they meet the following requirements:
--They have the same data type.
--They have the same value.
--They have the same named attributes. (The attributes can be in any order.)
Strangely, two variants with the same named attributes can have different values for those attributes and still be declared equal. These rules are different from LabVIEW clusters, which coerce data to the same data type before making a comparison.
- When casting to the variant type, make sure you receive the results you expect. LabVIEW is more forgiving when converting to the variant type than from it. If you wire an array of I32s to a subVI that has an array of variants as an input, LabVIEW assumes that you really want to place one I32 element in each variant element. But conversion back to strictly typed data based on a model array of I32s fails, because LabVIEW expects each variant element to contain an array of I32s. Prevent these sorts of errors by explicitly converting array elements to variants in a loop and using matched conversions to move to and from variants.
- Check for conversion errors when necessary. When a failed conversion causes a VI to behave incorrectly, make sure to check for type 91 errors when converting variants back to typed data. The variant conversion routines in LabVIEW are not very generous and some conversions might fail. For example, variants of the Boolean type cannot be converted to integer types, and your VI is probably not expecting a variant "true" to evaluate to the integer 0!
Flattening the variant to a string shows that it is a concatenation of the type and data strings of a variable along with the attribute data, but a full analysis is outside the scope of this article. The flattened format of most variables is described by NI's Application Note 154.
Native ActiveX and Visual Basic variants are 16 byte structures than can be either scalar data or references to data. When an ActiveX object passes a variant to LabVIEW, sometimes LabVIEW leaves the data as a reference that is embedded in the LabVIEW variant. This embedded data is an ActiveX variant. For the most part, the distinction between a normal LabVIEW variant and one containing an ActiveX variant can be ignored. LabVIEW knows how to handle the reference and the Variant to Data function returns a value as expected. But there are some subtle differences.
First, ActiveX variants are meaningless when flattened: the data returned is always 16 bytes of zeros. Thus, while you can flatten and then unflatten a LabVIEW variant without damage (for example, to pass the variant in a queue), this does not work with an ActiveX variant. This applies to the Variant to Flattened String function as well. Always convert such variants to normal data types and then to strings.
Second, ActiveX variants can cause serious performance issues when used in ways that are otherwise acceptable LabVIEW style. Normally, displaying the first few cells of a large 2D LabVIEW array generates a negligible performance penalty, because the user interface thread only has to update a few controls with new values. But display of a few cells in a large array of ActiveX variants (such as a database query) can be very slow and sometimes causes LabVIEW to crash. We’re not entirely sure what causes the problem, but it seems to affect only visible controls and not those in a subVI. (Deleting an indicator reduced a 26,000-element database query from 240 seconds to 7.) Until NI addresses this, be careful with large arrays of ActiveX variants.
LabVIEW variants resemble flattened strings more than variants in Visual Basic. There is still no way to decode or use them in LabVIEW functions without knowing the data type in advance. While they do not have the flexibility of variants in some other languages, they provide a higher-level way of working with generic data than flattened strings. They give the programmer more confidence that values passed to and from ActiveX objects will be parsed correctly. The use of variants in setting the value of controls by reference is cleaner and more convenient than type string plus data string inputs used by the VI Server to accomplish the same task. The VI Server should adopt the control reference approach.
The implementation of variants feels rough in places; comparisons and conversions do not behave like most programmers would expect them to, and there is no official way to determine the data type of a variant and thereby avoid data conversion errors. It is likely that variants will be improved in future releases of LabVIEW. As variants become more powerful, they will appear more commonly in LabVIEW programs, but one should still use strict typing (normal LabVIEW data types) whenever possible.
Robert B. Calhoun (firstname.lastname@example.org) is a LabVIEW consultant. Jason Dunham (email@example.com) is President of San Francisco Industrial Software, Inc., a National Instruments Alliance Program member. They have worked together on a variety of LabVIEW development projects.
Learn about the latest version of LabVIEW -- LabVIEW 6.1