Generics are a powerful feature that advanced LabVIEW developers have been requesting for a long time. If you don’t know what generics are, here is a description of the feature from Microsoft’s MSDN documentation:
[Generics] make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. For example, by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations…
In other words, generics allow you to develop APIs that are data type agnostic. The developer using your API gets to choose what type they wish to use with your functions. If you haven’t dealt with generics before, this might sound a lot like LabVIEW variants, but there is an important difference between variants and generics. Variants defer typing all the way to runtime, whereas generics require that the type be specified at compile time. This means that the compiler can check whether or not the user of your API is being consistent with the data type they have chosen.
For example, let’s say you want to make a linked list API. This API would allow users to add and remove elements in a linked list. But let’s say you want to enforce that all elements in the linked list are the same data type. The only way to accomplish this in LabVIEW is to rewrite the entire API for every possible data type. You would need a numeric linked list, a boolean linked list, a string linked list, a waveform linked list, and so on. This is obviously cumbersome and tedious. In addition, how do you handle custom data types like clusters or enums? There’s no good answer.
What’s frustrating, is that LabVIEW clearly has this feature somewhere under the hood. Let’s take a look at the queue API, as an example. When creating a queue using ‘Obtain Queue.vi’, you are required to specify the data type of the queue via the ‘element data type’ input. From that point on, when enqueuing or dequeuing from the queue, the compiler can ensure that you use the correct data type (resulting in a broken wire and broken run arrow if you don’t comply). Just like our linked list example, the queue API clearly implements generics, since it enforces type consistency at compile time, while allowing the user to use any data type (including clusters, classes, enums, etc).
It’s unfortunate that this feature seems to be present at some level in LabVIEW, but is not exposed to the developer. I’m sure if it were feasible to do so, NI would have exposed this functionality by now. I think there might be ways to get close to generic-like functionality using xnodes, but honestly I haven’t had enough experience with that particular form of LabVIEW black magic to say for sure. Perhaps that will be the topic of a future post. Until then, if you’ve ever used xnodes to try and implement generics, feel free to comment below!
This feature is present, and you can use it, almost… Only, it’s a giant PITA, you have to create a set of polymorphic vi’s that have the desired connector pane and duplicate the logic, or call a core subvi with variant inputs… So, it doesn’t save you any time like real generics would. Some people have written scripting magic to help this, https://lavag.org/files/file/53-example-create-polymorphic-vi/ I haven’t used them, but maybe it would help…
LikeLike
> I’m sure if it were feasible to do so, NI would have exposed this functionality by now.
That’s the important thing to understand. Primitive APIs, like the queue or array functions, aren’t really relevant, because their generic behavior is created using behind the scenes C code. NI has been aware of a long time and have made several attempts at supporting generics, going back at least 15 years, and most of those attempts actually exist in LV and can be used.
The key thing to notice here, though, is the “several attempts” part. My understanding is that the reason non of those attempts were ever made public was that they all had different issues. These would not necessarily manifest with simple code, but may become relevant with more nested calls, with dynamic calls, dynamic dispatch, etc., where things like type propagation may not work correctly. This is the main reason I haven’t used any of them in production code.
With that out of the way, here is a reasonably exhaustive list. You can find information on these by looking at places like the LAVA forums:
1. External nodes. These were relevant back in LV 7.x, but have basically been replaced by XNodes.
2. XNodes. These offer much much more functionality than just generics, but type adaptation is certainly one of their benefits.
3. Express VIs. These are really the only officially supported option on the list. While express VIs don’t do automatic type adaptation, you should be able to configure them to script the relevant code when double clicked. This is basically a much stupider version of XNodes and I wouldn’t recommend it for this use case.
4. Generics. With the right magic, you can mark controls on the connector pane as generic, which would cause their type to propagate.
5. VI Macros. Rename a VI’s extension to vim and LV will basically propagate types through the VI. As far as I can tell, this is the most supported one, but still isn’t an official feature.
LikeLike
This is great info, thanks for commenting!
LikeLike
Hey Robert, have you seen Malleable VIs (*.vim) in LabVIEW 2017? They are a pretty cool step in the right direction.
LikeLike
Hi Jim, yes Malleable VIs are definitely a welcome addition. The only shortcoming I can see is that the data type specification seems to be limited in scope to just that VI’s terminals. If there was a way to specify that the data type should apply to a collection of VIs (say a library or a class), then I think it would be an even more powerful feature.
LikeLike