Introduction to Malleable VIs in LabVIEW
In my first LabVIEW-related blog post, I wanted to talk about a new feature introduced in LabVIEW 2017 – Malleable VIs. I will give an introduction to Malleable VIs, discuss some of their benefits/limitations and show some examples of how Malleable VIs can improve code reuse. I’ll expand on this post with more examples in subsequent posts.
Malleable VIs are designed to implement ‘generics’ in G, a feature which can be found in other languages (e.g. C#), allowing you to create functions where the data type is not defined in the function and is determined either at compile or run-time. The use of generics allows you to improve code reuse since you can write a function once for many data types. If you find yourself writing (or copying+pasting) very similar VIs where only the data type on the connector pane is different, you probably want to have a look at Malleable VIs.
Before Malleable VIs, this was done in one of two ways:
Method 1 – Polymorphic VIs
The first method of supporting multiple data types is to use a Polymorphic VI, but I consider it to be cheating – essentially creating a separate VI for each data type you need to support. This leads to large numbers of VIs that have to be maintained, most of which are essentially the same but with different data types on the terminals to avoid coercion.
As an example, here is the OpenG Sort Array function. This is a polymorphic VI that sorts a 1D or 2D array of most LabVIEW datatypes (e.g. numeric types, strings, paths). This leads to 33 different VIs – the polymorphic VI itself and the ~32 VIs that support the different data types.
Of course, if you wanted to sort an array of a datatype not supported by the OpenG Sort Array function, such as clusters, enums etc. then you would need to create an additional Sort Array VI for your data type to do so.
Method 2 – Variant Polymorphism
If you look at the functions of the OpenG/MGI toolkits, there are many VIs which take in and pass out a variant. Since a variant is a generic data type (it can hold any type of data at run-time), LabVIEW will automatically coerce any data type to a variant at the connector pane terminal boundary. This allows you to wire anything into a variant terminal, perform some operation on the data and then return the variant out. Inside the VI, however, you must handle the different data types (e.g. by type-casting) and it also requires the user of your VI to type-cast the variant back to the initial data type.
Below is an example of how this method is currently employed in the OpenG Read INI Section VI. The VI has a variant in (to define the expected data type/default values) and returns a variant which then has to be converted back to the original type.
The Solution – Malleable VIs (.vim)
Introduced in LabVIEW 2017 was the new VI extension ‘.vim’ for Malleable VIs. With VIMs, the data types on the connector pane can adapt to any valid input (i.e. any data type where the VI can still compile / no broken run arrows).
In the following sections, I’ll show a basic and an intermediate example of Malleable VIs which should help to explain the concept.
Basics – Stall Data Flow.vim
I think most LabVIEW developers have at some point found themselves creating a VI like ‘Delay with Error Wires’ or ‘Tick Count with Error Wires’ for adding delays or benchmarking their code. Even if you haven’t, you’ve probably found yourself putting a sequence structure frame with a Delay primitive inside it and a wire going through. One of the Malleable VIs included in LabVIEW 2017 is Stall Data Flow.vim which replaces the ‘Delay with Error Wires’ with a VIM that can delay data flow on any wire type – making my VI ‘delay with error wires’ VI redundant (good!).
The content of the VIM is very simple – exactly the same as what’s in my ‘delay with error wires’ VI shown above but if you look at the examples at the bottom, any data type passed in is passed out without coercion dots – so you can use it with any data type. That’s the basic principle of the Malleable VI.
Intermediate – The Type Selector – Increment Array Element(s).vim
The next aspect of VIMs I want to introduce is the Type Selector. The type selector is a new structure specifically for VIMs that isn’t currently available on the palettes (as of LV2017 SP1 – probably coming in LV2018). It allows you to handle cases for specific input data types. The simplest example of which is to allow for a terminal to be a scalar as well as an array – as is the case in Increment Array Element.vim.
LabVIEW checks each case of the type selector until it reaches the first one that is valid (i.e. compiles), and it uses that as the implementation for the case. If a scalar is passed in, the VIM increments just the specified index and if an array of indexes is passed in, each element is incremented in a for loop.
To take it slightly further, there is also a ‘match type’ node which breaks the type selector case if the data types do not match, giving you another tool for handling specific cases in your VIM, but I won’t go into those here.
Advanced – Coming Soon!
There are also more advanced uses for VIMs (to do with using Malleable VIs as OOP interfaces, and some examples with sorting of arrays using custom sort functions) but I won’t go into those in detail as this is just an introduction. Stephen Loftus-Mercer from NI (AristosQueue) gave an excellent presentation at the CLA Summit in Madrid in which he showed many examples of Malleable VIs. There are some examples included in LV2017 SP1 and some more that should be shipping with LabVIEW 2018.
- examples\Malleable VIs\Basics\Malleable VIs Basics.lvproj
- examples\Malleable VIs\Class Adaptation\Malleable VIs – Class Adaptation.lvproj
If you have access to the CLA Community, he has posted a further example here.
My Example – INI Configuration API
One of the immediate use cases for Malleable VIs that jumped out at me was for creating a re-use library for handling INI configuration files. In many of my projects I use INI configuration files when I need a simple human-readable configuration file format. I use the OpenG configuration file functions, but I wrap them into a ‘Load Configuration’ and ‘Save Configuration’ VI with a type definition that contains the contents of the file. Here is what it usually looks like:
This VI loads the INI file and if it (or some keys) can’t be found, uses the default values specified. It then writes the settings back to the file (which is useful to create the file on initialisation of the software). I create a ‘project-specific’ version of this VI (i.e. with a different cluster type definition) since I want to keep my top level block diagram (where I load the settings) simple.
Now, with Malleable VIs I have created the following:
I now have a reusable API for loading/saving INI configuration files (with the initialisation code), I can easily see the path/default values on the calling VI but I don’t have to convert the variant back to my settings type on the calling VI (and no coercion dots!).
Malleable VIs currently have one major limitation – they must be set to ‘inline’ in the execution properties. While this might not seem like a big deal at first (e.g. you have to disable debugging, no automatic error handling and must be shared/preallocated reentrant), this is actually more of a limitation than you might think – some LabVIEW nodes cannot be used in inlined VIs. According to the LabVIEW documentation, inline VIs cannot contain ‘certain’ block diagram nodes – the help mentions Property Nodes and Invoke Nodes (but there are others – I couldn’t find a list!). There is a flat restriction that inlined VIs cannot contain property nodes or invoke nodes (this includes class property nodes, even though the accessor VIs can be inlined). This unfortunately rules out many potential use cases for them – for example for UI manipulation.
Below is an example of something I thought would be a great candidate for a Malleable VI – something which sets VI properties on a bunch of control references. This Malleable VI would allow me to wire in either a scalar or array of control/indicator references and set some property (e.g. visible, disabled etc.) on all of them at once.
There is a LabVIEW Ideas exchange post about the issue, but with the inlining restriction of Malleable VIs I think it is more relevant now than ever so please go ahead and +1 it.
I think Malleable VIs are a really neat feature included in LabVIEW 2017 and I hope that this post goes some way in helping you to understand them and unlock their potential in your LabVIEW programming. It was a bit of a shame to discover I can’t use them to replace some of my UI utility functions, but I see that there are still plenty of other uses for them.
I’m very interested to hear/see how others are using Malleable VIs in their code – so if you have any examples/ideas then it would be great to hear them – please leave a comment below!
Thanks for this interesting article – especially I liked example of malleable VIs usage for ini files parsing… It inspired me to play a bit with malleable VIs for classes, so let me share my thoughts about it – http://kosist.org/2018/07/malleable-vis-class-adaptation-or-how-to-create-universal-class-data-accessor/. I will be very happy to hear any feedback from you regarding of usage of malleable VIs as class accessors…