rusty icon indicating copy to clipboard operation
rusty copied to clipboard

Function pointer

Open Vladimir19900828 opened this issue 2 years ago • 10 comments

Hello. Are you planning to add a function pointer?

Vladimir19900828 avatar Nov 02 '23 12:11 Vladimir19900828

He sorry for the late reply on this, we don't currently support function pointers no. You can always use a Function_Block pointer as a parameter, but this won't allow you an on the fly definition of your function.

ghaith avatar Nov 07 '23 09:11 ghaith

could you post some example ST code on how you would use this?

riederm avatar Nov 09 '23 08:11 riederm

@ghaith function pointers would be extremely useful. Is that going to be on the agenda any time soon?

zeehans avatar Jan 28 '24 19:01 zeehans

@zeehans They are not currently planned, but mainly because we can't see a clear usecase where classes/interfaces would not fit here. Our main issue is how do we use it: how do you declare a function pointer? In other languages the format of your parameter allows such a definition. In PLC the parameters can get a bit complex, especially with multiple blocks of VAR_INPUT, VAR_OUTPUT and VAR_IN_OUT which is why we would like some input on how this could look like. Do you have to pre-declare a function template that you can then pass? Do you inline it and only allow a single block? Seen technically it is not much trouble to implement them on the codegen side, as long as we can offer good parsing and validation. I think we could fit this in as part of the Object Oriented support as interfaces would be similar. This is planned for the next few months but no concrete date yet.

ghaith avatar Jan 29 '24 12:01 ghaith

@ghaith I agree that interfaces handle pretty much the same use case and function pointers would only help developers who prefer not to use OOP. I can only speak for ourselves as we may be moving our C based PLC source code to ST so as to gain more flexibility as far as hardware is concerned. Unfortunately not many manufacturers support C. When we move across we'd like to stay as close to the C source as possible so function pointers would be really handy for us here.

In codesys function pointers are implemented like so:

FUNCTION F_Add1: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add1:= param + 1;
END_FUNCTION

FUNCTION F_Add2: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add2:= param + 2;
END_FUNCTION

// declare only
FUNCTION FPTR_Add : INT
VAR_INPUT
  param : INT;
END_VAR
END_FUNCTION

F_FPTR_Assign
VAR_INPUT
  dst : POINTER TO PVOID;
  src : POINTER TO PVOID;
END_VAR
dst^ := src^;
END_FUNCTION

FUNCTION main: INT
F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add1));

// FPTR_Add points to F_Add1
val := FPTR_Add(val);
// here val has value 1

F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add2));

// FPTR_Add points to F_Add2
val := FPTR_Add(val);
// here val has value 3

F_Test := val;
END_FUNCTION

zeehans avatar Jan 29 '24 12:01 zeehans

@ghaith I agree that interfaces handle pretty much the same use case and function pointers would only help developers who prefer not to use OOP. I can only speak for ourselves as we may be moving our C based PLC source code to ST so as to gain more flexibility as far as hardware is concerned. Unfortunately not many manufacturers support C. When we move across we'd like to stay as close to the C source as possible so function pointers would be really handy for us here.

In codesys function pointers are implemented like so:

FUNCTION F_Add1: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add1:= param + 1;
END_FUNCTION

FUNCTION F_Add2: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add2:= param + 2;
END_FUNCTION

// declare only
FUNCTION FPTR_Add : INT
VAR_INPUT
  param : INT;
END_VAR
END_FUNCTION

F_FPTR_Assign
VAR_INPUT
  dst : POINTER TO PVOID;
  src : POINTER TO PVOID;
END_VAR
dst^ := src^;
END_FUNCTION

FUNCTION main: INT
F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add1));

// FPTR_Add points to F_Add1
val := FPTR_Add(val);
// here val has value 1

F_FPTR_Assign(ADR(FPTR_Add), ADR(F_Add2));

// FPTR_Add points to F_Add2
val := FPTR_Add(val);
// here val has value 3

F_Test := val;
END_FUNCTION

Thank for the clarification. I did not know that codesys supports function pointers. We would like to keep some compatibility with codesys so this issue just got a higher priority :)

I would suggest the following example as an alternative to avoid assigning the original pointer:

{pointer} //We could probably also skip this
FUNCTION FPTR_Add : INT
VAR_INPUT
  param : INT;
END_VAR
END_FUNCTION

FUNCTION F_Add1: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add1:= param + 1;
END_FUNCTION

FUNCTION F_Add2: INT
VAR_INPUT
  param : INT;
END_VAR
F_Add2:= param + 2;
END_FUNCTION

FUNCTION main: INT
VAR
 f_ptr : POINTER_TO FPTR_ADD; //can also be REF_TO for standard compliance
//If we are passing the function we could also just use VAR_INPUT {ref} or VAR_IN_OUT for auto pointer functionality
END_VAR

f_ptr := ADR(F_Add1); //Can also be &F_Add1 or REF(F_Add1) for standard compliance 
//If using VAR_IN_OUT or VAR_INPUT {ref} we cal use f_ptr := F_Add1 here
// FPTR_Add points to F_Add1
val := f_ptr(val);
// here val has value 1


f_ptr := ADR(F_Add2); //Can also be &F_Add2 or REF(F_Add2) for standard compliance 
//If using VAR_IN_OUT or VAR_INPUT {ref} we cal use f_ptr := F_Add2 here
// FPTR_Add points to F_Add2
val := f_ptr(val);
// here val has value 3

F_Test := val;
END_FUNCTION

ghaith avatar Jan 29 '24 14:01 ghaith

@ghaith yes I just stumbled across the SO link this weekend as I didn't see function pointers being documented anywhere. I found the implemention surprisingly intuitive coming from C. Your suggestion works for me!

zeehans avatar Jan 29 '24 14:01 zeehans

Codesys function pointers are not officially supported as far as I am aware.

They also don't show in the call stack and aren't type-safe.

I don't think function pointers are helpful by themselves.

Function pointers to Class/FB methods on the other hand would be helpful, because then you don't need to create a standalone POU type to for every implementation. This would be helpful in ViewModel code (User Interface view backing types) for implementing call back functions / events.

Further, it would be even more helpful, coupled with anonymous functions/ lamdas. This would really help improve composition in the language, right now Inheritance is forced to be abused far too to accomplish things that would normally be handled with call backs and or lamdas in other newer languages.

You guys should join the SASE Slack workplace (Society for Automation Software Engineers) https://Sase--space.slack.com Website is https://sase.space Here is an invite link that is good for 30 days https://join.slack.com/t/sase--space/shared_invite/zt-2ca4osdk3-8CgXlBVG2wmlY0RSOC5O9A

Some of us were just talking about the problems with the IEC standard, and it's lack of modern language features.

It is a very antiquated language, and it is holding the industry back.

Lastly in my opinion, the implementation of Properties for Function Blocks, Classes, and Interfaces would be something that you should add first before taking function pointers on. Properties are a VERY heavily used feature in Codesys, that is not included in the IEC standard. Then you could come back and look at this.

TheColonel2688 avatar Feb 08 '24 02:02 TheColonel2688

@TheColonel2688 I agree with everything you said. Developing with IEC languages for PLCs is an absolutely horrific experience and limiting in many regards. And yes - lambdas would be great. Nevertheless I suppose the main purpose of this project is to be able to take existing PLC code and use it for PC simulation purposes without having to maintain a side project in a non-IEC language. At least that's what we are using it for. Lambdas don't really help here as no IEC compliant PLC controller manufacturer/vendor supports them.

Codesys actually does officially support function pointer if you read the documentation of the ADR operator carefully:

ADR yields the address of its argument in a DWORD.

In particular,

As opposed to CoDeSys V2.3, you can use the ADR operator with function names, program names, function block names, and method names. Therefore, ADR replaces the INDEXOF operator. When using function pointers, ...

So this officially works:

PROGRAM FPtrTest
VAR	
	foo: POINTER TO DWORD;
	foo2: POINTER TO DWORD;
END_VAR

foo:=ADR(FooImplementation);
foo2:=ADR(FooDeclarationOnly);
foo2^:=foo^;
FooDeclarationOnly();

zeehans avatar Mar 04 '24 13:03 zeehans

Oh I guess I was wrong, my bad.

If you do end up implementing this feature, it would be really nice if you built some type safe syntax on top of it.

To do this you would need to allow the a function prototype to be defined with the function pointer.

Look at C# delegates for inspiration.

Yes this would be more complicated but it is more in the spirit of PLC programming where we are trying to create very safe and stable code.

TheColonel2688 avatar Aug 04 '24 00:08 TheColonel2688