Why use pointers and reference in codesys V3? - pointers

My question is: what are the benefits of using pointers and reference to?
I am new to codesys and in my previous job, I programmed in TIA portal (Siemens) and Sysmac Studio (Omron) and never came across pointers or something similar. I think I understand how they work but not sure when I should be using them myself.
For example, I just received a function block from a supplier:
Why don't they just have an array for input and output?

First of all, if you have ever used the VAR_IN_OUT declaration, then you have already used references, since that is equivalent to a VAR with REFERENCE TO.
As for the uses, there are mainly 4 that I can think of right now:
Type Punning, which you can also achieve using a UNION, but you may not want to have to create a union for every single reinterpretation cast in your code.
TL; DR: To save memory and copy execution time. Whenever you pass some data to a function/function block, it gets copied. This is not a big problem if your PLC has enough CPU power and memory, however if you are dealing with especially huge data on a low end PLC, then you may either exceed real time execution constraints, or run out of memory. When you pass a pointer/reference however, no matter how big the data is only the pointer/reference gets copied and passed, which is 4 bytes in 32 bit system, and 8 bytes in a 64 bit one.
In C style languages you'd use pointers/references when you want a function to return multiple values without the hassle of creating a custom structure every time. You can do the same here to, however in CODESYS function can have multiple outputs, for example:
VAR_OUPUT
out1 : INT; (*1st output variable *)
out2 : INT; (*2nd output variable *)
//...
END_VAR
And finally, as I mentioned at the very beginning, when you want to pass some data that needs to be modified in the function itself, in other words, where you can use VAR_IN_OUT you can also use pointers/references. One Special case where you will have to use a pointer is if you have a Function Block that receives some data in the FB_Init (initialization/construction) function and stores it locally. In such case you would have a pointer as a local variable in the function block, and take the address of the variable in the FB_Init function. Same applies if you have a structure that needs to reference another structure or some data.
PS. There are probably some other uses I missed. One of the main uses in other languages is for dynamic memory allocations, but in CODESYS this is disabled by default and not all PLCs support it, and hardly anyone (that I know) uses it.
EDIT: Though this has been accepted, I want to bring a real life example of us using pointers:
Suppose we want to have a Function Block that calculates the moving average on a given number series. A simple approach would be something like this:
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..50] OF INT;
currentIndex: UINT;
END_VAR
However, this has the problem that the moving window size is static and predefined. If we wanted to have averages for different window sizes we would either have to create several function blocks for different window sizes, or do something like this:
FUNCTION_BLOCK MyMovingAvg
VAR CONSTANT
maxWindowSize: UINT := 100;
END_VAR
VAR_INPUT
nextNum: INT;
windowSize: UINT (0..maxWindowSize);
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
window: ARRAY [0..maxWindowSize] OF INT;
currentIndex: UINT;
END_VAR
where we would only use the elements of the array from 0 to windowSize and the rest would be ignored. This however also has the problems that we can't use window sizes more than maxWindowSize and there's potentially a lot of wasted memory if maxWindowSize is set high.
There are 2 ways to get a truly general solution:
Use dynamic allocations. However, as I mentioned previously, this isn't supported by all PLCs, is disabled by default, has drawbacks (you'll have to split you memory into two chunks), is hardly used and is not very CODESYS-like.
Let the user define the array of whatever size they want and pass the array to our function block:
FUNCTION_BLOCK MyMovingAvg
VAR_INPUT
nextNum: INT;
END_VAR
VAR_OUTPUT
avg: REAL;
END_VAR
VAR
windowPtr: POINTER TO INT;
windowSize: DINT;
currentIndex: UINT;
END_VAR
METHOD FB_Init: BOOL
VAR_INPUT
bInitRetains: BOOL;
bInCopyCode: BOOL;
END_VAR
VAR_IN_OUT // basically REFERENCE TO
window_buffer: ARRAY [*] OF INT; // array of any size
END_VAR
THIS^.windowPtr := ADR(window_buffer);
THIS^.windowSize := UPPER_BOUND(window_buffer, 1) - LOWER_BOUND(window_buffer, 1) + 1;
// usage:
PROGRAM Main
VAR
avgWindow: ARRAY [0..123] OF INT; // whatever size you want!
movAvg: MyMovingAvg(window_buffer := avgWindow);
END_VAR
movAvg(nextNum := 5);
movAvg.avg;
The same principle can be applied to any function block that operates on arrays (for example, we also use it for sorting). Moreover, similarly you may want to have a function that works on any integer/floating number. For that you may use one of the ANY types which is basically a structure that holds a pointer to the first byte of the data, the size of the data (in bytes) and a type enum.

Related

(CAPL) how i assign array length by using parameter

void func(int a){
byte arr[a];
}
this code is not working. how I assign array length by using parameter?
In CAPL you have many options to go, but first you'll have to consider you probably want to step back and ask yourself if you really need variable array size at runtime. The measurement performance is what you should be concerned about, declaring a suitable array size as design may be a safer approach.
A global array of parametric size could be something like this:
variables
{
int arraySize = 256;
byte arr[arraySize];
}
From the docs,
Declaration of arrays (arrays, vectors, matrices) is permitted in CAPL. They are used and initialized in a manner analogous to C language.
In C, array size is constant:
Array is a type consisting of a contiguously allocated nonempty sequence of objects with a particular element type. The number of those objects (the array size) never changes during the array lifetime. [source]
This is why your code is not working: you cannot create an array of runtime-based size. Similarly, from the same source
Variable-length arrays
If expression is not an integer constant
expression, the declarator is for an array of variable size.
Each time the flow of control passes over the declaration, expression
is evaluated (and it must always evaluate to a value greater than
zero), and the array is allocated (correspondingly, lifetime of a VLA
ends when the declaration goes out of scope). The size of each VLA
instance does not change during its lifetime, but on another pass over
the same code, it may be allocated with a different size.
This is why you should be able to define a parametric array like I showed you. Even if in the code arraySize should change, arr will be of 256 elements for the execution of your CAPL script.
void func(int a){
byte arr[a];
}
Will throw error, because int a is determined to be of non-constant time, thus violating the requirements above. What you can do, is to memcpy parts of a larger array to a location of choice, for example a smaller array, or employ a number of "buffer" arrays as you often see in CAPL scripts.
As I took it home, the gist of it is: use a larger size array, and be precise about where you are putting your information inside of it. Note that you must be precise, because every element in the array contains some kind of data, at init most of it is non-sense, and there is no safeguard for you against this digital noise.

How should I translate char dbcc_name[1] to Delphi?

I have a problem to translate the following C++ code to Delphi.
This is the code:
char dbcc_name[1]
And this is what I think what it should be:
dbcc_name : array [0..0] of Char;
However, I know this field should return a name, and not just one character.
So, it maybe something like this:
dbcc_name: array of Char;
Now, this looks nice, but there's no way of predicting how long the name will be, and it will probably return something with a load of rubish and somewhere a #0 terminator in it, but that is -I think- not the proper way.
Would it not be wise to use a pointer to this array?
Like:
dbcc_name: PChar;
Thank you in advance.
You were right the first time, only with the wrong data type. Use AnsiChar instead, which is char in C/C++:
dbcc_name: array[0..0] of AnsiChar;
In Delphi 2009+, Char is an alias for WideChar, which in C/C++ is wchar_t on Windows and char16_t on other platforms.
That being said, in C/C++, it makes sense for a 1-element array to exist in a struct when it represents variable-length data, and is the last field in the struct. In this case, the struct usually exists inside of a larger block of allocated memory. There is no array bounds checking in C/C++, the contents of an array can exceed the bounds of the array as long as it doesn't exceed the bounds of the memory that the array is allocated in. Referring to an array by name decays into a pointer to the first element. It is very common in C to exploit this to define a struct that has variable-length data embedded directly inside of it, that can be referred to by name, without having to allocate the data elsewhere in memory. This is especially useful in embedded systems with limited memory.
There are several structs in the Win32 API that use this approach for variable-length data. Raymond Chen discusses this in more detail on his blog:
Why do some structures end with an array of size 1?
You will most likely use either array[0..0] of char or just char, with some caveats. The code below assumes you are using a Windows API and I make assumptions based on one specific Windows Message record that matches your description.
If you are using char dbcc_name[1] as defined in DEV_BROADCAST_DEVICEINTERFACE in C its a char in the DEV_BROADCAST_DEVICEINTERFACE_A structure, but a wchar_t in the DEV_BROADCAST_DEVICEINTERFACE_W structure. NOTE: char in C maps to AnsiChar in Delphi and wchar_t maps to char in Delphi.
With the W strucutre I declare this in Delphi as dbcc_name: char; to read, I simply use PChar(#ARecordPtr^.dbcc_name). Your C++ sample seemingly uses the A struct, a straight translation to Delphi would mean a using the A structure with AnsiChar and using PAnsiChar to read, just replace in the code above.
However, a new Delphi project will by default use the Unicode version (or W imports) of a Windows API so that is why my sample below is written for Unicode.
In my implementation I simply have it defined as char. Some developers like the array[0..0] of char syntax because it leaves a clue of variable length array at that position. It is a more accurate translation, but I find it adds little value.
Example:
PDEV_BROADCAST_DEVICEINTERFACE = ^DEV_BROADCAST_DEVICEINTERFACE;
DEV_BROADCAST_DEVICEINTERFACE = record
dbcc_size: DWORD;
dbcc_devicetype: DWORD; // = DBT_DEVTYP_DEVICEINTERFACE
dbcc_reserved: DWORD;
dbcc_classguid: TGUID;
dbcc_name: Char; // <--- [HERE IT IS]. Use AnsiChar is using the A record instead of the W Record
end;
and to use it
procedure TFoo.WMDeviceChange(var AMessage: TMessage);
var
LUsbDeviceName: string;
LPDeviceBroadcastHeader: PDEV_BROADCAST_HDR;
LPBroadcastDeviceIntf: PDEV_BROADCAST_DEVICEINTERFACE;
begin
if (AMessage.wParam = DBT_DEVICEARRIVAL) then
begin
LPDeviceBroadcastHeader := PDEV_BROADCAST_HDR(AMessage.LParam);
if LPDeviceBroadcastHeader^.dbch_devicetype = DBT_DEVTYP_DEVICEINTERFACE then
begin
LPBroadcastDeviceIntf := PDEV_BROADCAST_DEVICEINTERFACE(LPDeviceBroadcastHeader);
LUsbDeviceName := PChar(#LPBroadcastDeviceIntf^.dbcc_name); // <--- [HERE IT IS USED] Use PAnsiChar if using the A Record instead of the W Record
...
end;
end;
end;
See more in my post on pointers and structures and for more explanation on the odd use of a single character array see the the "Records with Variable Length Arrays" section in my post on arrays and pointer math.

Why is fmt.Println not consistent when printing pointers?

I'm an experienced programmer but have never before touched Go in my life.
I just started playing around with it and I found that fmt.Println() will actually print the values of pointers prefixed by &, which is neat.
However, it doesn't do this with all types. I'm pretty sure it is because the types it does not work with are primitives (or at least, Java would call them that, does Go?).
Does anyone know why this inconsistent behaviour exists in the Go fmt library? I can easily retrieve the value by using *p, but for some reason Println doesn't do this.
Example:
package main
import "fmt"
type X struct {
S string
}
func main() {
x := X{"Hello World"}
fmt.Println(&x) // &{Hello World} <-- displays the pointed-to value prefixed with &
fmt.Println(*(&x)) // {Hello World}
i := int(1)
fmt.Println(&i) // 0x10410028 <-- instead of &1 ?
fmt.Println(*(&i)) // 1
}
The "technical" answer to your question can be found here:
https://golang.org/src/fmt/print.go?#L839
As you can see, when printing pointers to Array, Slice, Struct or Map types, the special rule of printing "&" + value applies, but in all other cases the address is printed.
As for why they decided to only apply the rule for those, it seems the authors considered that for "compound" objects you'd be interested in always seeing the values (even when using a pointer), but for other simple values this was not the case.
You can see that reasoning here, where they added the rule for the Map type which was not there before:
https://github.com/golang/go/commit/a0c5adc35cbfe071786b6115d63abc7ad90578a9#diff-ebda2980233a5fb8194307ce437dd60a
I would guess this had to do with the fact that it is very common to use for example pointers to Struct to pass them around (so many times you'd just forget to de-reference the pointer when wanting to print the value), but no so common to use pointers to int or string to pass those around (so if you were printing the pointer you were probably interested in seeing the actual address).

In Golang, can I customise key comparison, if I am taking struct as key?

How does map in Golang compare keys? For some reason, I need to have a struct as a key, which has 2 values inside. I want map to compare by only first value, not second. Second is for my usage. Like in java, I can customise equals method, so map will take only logically equal keys within. Is there any way to do that?
Edit: Looks like there is no way to do that. So I am now putting down my problem here. Please help me to think in 'Go-way'.
So, I want to implement a 'timed map', which tracks the key insertion time. In other words, there is a map which accepts and processes the values. Now, if the data in map is older than some specific time-interval, then I should clear it out.
So, I thought of having a key struct which has id and timestamp. When a new key comes, map takes it with id and currentTimeInMillis. After sometime, if a key comes which already exists, then map should preserve the first insertion time and only updates the value array.
To process, I will have a looping over map and check if any particular key is inside for more than threshold limit, then I clear it out. I can have this timestamp in value array, but that also has a timestamp of its own, so putting one more might confuse someone else.
Please suggest something.
Put the time on your value. Here's some example of how to structure your data.
type DataObj struct {
Id int
Updated time.Date
// other fields
}
m := map[int]DataObj{}
m[d.Id] = d // assign using the id as your key
for k, v := range m {
if time.Since(v.Updated) > duration {
delete(m, k) // remove the stale item
}
}
// some logic like this for adding/overwriting
v, ok := m[newObj.Id]
if ok { // an element with this id existed
if time.Since(v.Updated) > duration {
m[v.Id] = newObj // assign new value over old one
}
}
I can't provide anything much more specific because you don't have any code with which to work. It seems like you'd probably like some of this (like the remove bits) to run on a timer. To do that, invoke the function as a goroutine and use a timer so every X seconds it unblocks and removes items from the map. If you're doing this you also need to use a mutex so the calling scope doesn't access the map while the remove function running the background is filtering out old items.
The overwrite bit is really straight forward, just test if the item is in the map, check it's time stamp, if it's beyond the threshold assign the new value, if not do nothing.
The main thing to take away here is to not use a struct for your key... There is no reason to do object equality, your object has an id, us it as your key. Everything else you care about can be held on the value (even the key itself is). As someone pointed out this isn't Java and even if it were, equality overrides in C# and Java are literally a fucking nightmare.

What is the difference between Vec<struct> and &[struct]?

I often find myself getting an error like this:
mismatched types: expected `collections::vec::Vec<u8>`, found `&[u8]` (expected struct collections::vec::Vec, found &-ptr)
As far as I know, one is mutable and one isn't but I've no idea how to go between the types, i.e. take a &[u8] and make it a Vec<u8> or vice versa.
What's the different between them? Is it the same as String and &str?
Is it the same as String and &str?
Yes. A Vec<T> is the owned variant of a &[T]. &[T] is a reference to a set of Ts laid out sequentially in memory (a.k.a. a slice). It represents a pointer to the beginning of the items and the number of items. A reference refers to something that you don't own, so the set of actions you can do with it are limited. There is a mutable variant (&mut [T]), which allows you to mutate the items in the slice. You can't change how many are in the slice though. Said another way, you can't mutate the slice itself.
take a &[u8] and make it a Vec
For this specific case:
let s: &[u8]; // Set this somewhere
Vec::from(s);
However, this has to allocate memory not on the stack, then copy each value into that memory. It's more expensive than the other way, but might be the correct thing for a given situation.
or vice versa
let v = vec![1u8, 2, 3];
let s = v.as_slice();
This is basically "free" as v still owns the data, we are just handing out a reference to it. That's why many APIs try to take slices when it makes sense.

Resources