Working with VARIANTs
- Ivaylo Fiziev
- Aug 26
- 3 min read

VARIANT is yet another data type that you can use in your functions / function blocks. Usually it comes in handy when defining the input/output interface of the block. But what does it bring to the table? Why is it needed? Well the idea is to be able to reuse the block while having inputs/outputs of different data types. The types are determined at runtime instead of being hardcoded in the definition of the block. Seems weird but there are cases where this can be really useful. This means that you can call the block providing any type of argument and the SCL interpreter won't complain. In the general case it would report a type mismatch error. So VARIANTs provide a way to suppress the type checks.
But how do you get the actual types? Simple. You call a function that takes the variable on input and returns a type identifier. Then you compare the type identifier to a well known value. What is this value exactly? It can be a constant (INT, DINT, REAL etc.) or the type identifier of another variable.
Examples:
// example 1
if TypeOf(#in) = INT then
; // do something
end_if;
// example 2
if TypeOf(#in) = TypeOf(#var) then
; // do something
end_if;
Note: The TypeOf function does not work for arrays. If you have an array then use the TypeOfElements function instead.
If you try to use a variable of type VARIANT in an expression you'll hit a wall. They cannot be used this way. A 'malformed expression' error will be reported. Why is that? To evaluate the expression the interpreter relies on the type of the variable. With VARIANTs the type is unknown hence the expression cannot be evaluated. Before using the value you must copy it to a variable of a well known type. Then use this variable in the expression. How do you do that? Simple. Use another function.
Example:
if TypeOf(#in) = TypeOf(#var) then
VariantGet(SRC:=#in, DST=>#var); // copy the value to the variable
#out := #var ** 2; // calculate the expression
end_if;
Notice the sequence of actions. First you make sure the VARIANT and the variable are of the same type. Then you copy the value within the VARIANT to the variable. If you skip the first check and the types do not match you'll get a 'type mismatch' error.
Note: The VariantGet function does not work for arrays or UDTs!
How to proceed in the case where the VARIANT is an output parameter of the block. How to set its value? Simple. Use another function.
Example:
if TypeOf(#var) = TypeOf(#out) then
VariantPut(SRC:=#var, DST=>#out); // copy the variable to the output
end_if;
Note: The VariantPut function does not work for arrays or UDTs!
Some other functions related to the VARIANT data type:
IS_ARRAY - returns true if the VARIANT points to an array. false otherwise
CountOfElements - returns the total number of elements in an array. zero if the input is not an array
When dealing with arrays or UDTs there is a family of 'move' functions (MOVE_BLK, MOVE_BLK_VARIANT etc.) that support them. For now Process Simulate does not have these. The once mentioned above will be available in version 2512.
Lets look into a complete example. This is a simplified function that checks if the types of its arguments match:
FUNCTION "TYPE_EQUAL" : BOOL
VERSION:1.0
VAR_INPUT
A:VARIANT;
B:VARIANT;
END_VAR
BEGIN
if IS_ARRAY(#A) and IS_ARRAY(#B) then
#TYPE_EQUAL := TypeOfElements(#A) = TypeOfElements(#B) and CountOfElements(#A) = CountOfElements(#B);
else
#TYPE_EQUAL := TypeOf(#A) = TypeOf(#B);
end_if;
END_FUNCTION
Save it to a UTF8 encoded .scl file, import it into the SCL vault (Import SCL block command) and you can use it in your code like any other function. It works on arrays, UDTs and primitive data types. Nice!
Comments