Function overloading with ANY_<type>

unzu
2021-07-07
2021-07-09
  • unzu - 2021-07-07

    Hello,

    I am trying to understand if it's possible to do function overloading in codesys.

    Simple example: I want to create a function SQ(x) to calculate the square of x (x*x).
    I could create a SQ_INT, SQ_REAL, SQ_LREAL, ... for every needed datatype - a very ineffiecient way which would flood my project with many unneccessary functions that I would have to maintain.

    My investigations lead me to the ANY_-types like ANY_NUM and ANY_REAL.
    Correct me if I am wrong:
    1) I can use them to pass values of the corresponding types to my function: ANY_REAL would accept REAL and LREAL.
    2) Inside my function I can write specific code for every type by checking the type with x.TypeClass

    So far so good - what I need is to change my return-type to the corresponding input type.
    The square of an INT should return an INT, REAL should return REAL.

    Is that possible? I feel like there is some way to achieve that: When calculating the squareroot of a REAL with SQRT(), codesys automatically passes me a REAL and calculating the SQRT() of a LREAL delivers a LREAL. I even get warnings when I try to assign the SQRT(LREAL) to a REAL without explicit conversion because of possible dataloss.

    How does this work? And can I also use this mechanism?

    Thank you.

     
  • tvm - 2021-07-07

    what if you did this?

    FUNCTION SQ
    VAR_INPUT
        In:    ANY;
        Out:   ANY;
    END_VAR
    

    since the ANY type gives you the size, type, and a pointer to the value, you can write your result to the pointer at Out.pValue. Create a bunch of pointers to whatever data types you want to use, for example:

    VAR
       pRealResult:  POINTER TO REAL;
    END_VAR
    
    pRealResult:= Out.pValue;  //write your REAL answer to pRealResult^
    
     
    πŸ‘
    1
  • ojz0r - 2021-07-08

    Otherwise make one block with all the different inputs (int, real, uint etc).
    Then just use one of the inputs and leave the others at 0 and if input_type > 0 convert to real and do the calculation.
    Do the same with the outputs, convert the result to every type you want and then you can use whatever you need for the moment.
    This wont work as return value though.

     
  • nothinrandom - 2021-07-09

    @unzu,

    There are multiple ways as suggested here, you could create a function with multiple datatypes for INs and OUTs, initialize them with 0, and then select whichever data type you need to use. Another method is to leverage ANY like what TVM pointed out. So for example, the below allows you to take two variables and multiple them to gain output using ANY datatype. You could modify the below to mix and match certain datatypes if needed.

    FUNCTION Multiply : BOOL
    VAR_INPUT
        aVar1       : ANY;
        aVar2       : ANY;
        aOut        : ANY;
    END_VAR
    VAR
        _pbOut      : POINTER TO BYTE;
        _liVar1     : LINT;
        _liVar2     : LINT;
        _uliVar1    : ULINT;
        _uliVar2    : ULINT;
        _lrVar1     : LREAL;
        _lrVar2     : LREAL;
        _psiVar     : POINTER TO SINT;
        _piVar      : POINTER TO INT;
        _pdiVar     : POINTER TO DINT;
        _prVar      : POINTER TO REAL;
        _rResult    : REAL;
    END_VAR
    
    =========================================================================================
    
    // if OUT is unsigned and IN1/IN2 is signed, then raise error
    IF ((aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_USINT OR
        aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_UINT OR
        aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_UDINT OR
        aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_ULINT) AND
        (aVar1.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_SINT OR
        aVar1.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_INT OR
        aVar1.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_DINT OR
        aVar1.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_LINT OR
        aVar2.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_SINT OR
        aVar2.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_INT OR
        aVar2.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_DINT OR
        aVar2.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_LINT)) THEN
        RETURN;
    END_IF
    
    // if OUT is REAL and input is not
    IF ((aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_REAL) AND
        (aVar1.TypeClass <> __SYSTEM.TYPE_CLASS.TYPE_REAL OR
        aVar2.TypeClass <> __SYSTEM.TYPE_CLASS.TYPE_REAL)) THEN
        RETURN;
    END_IF
    
    // if OUT is LREAL and input is not
    IF ((aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_LREAL) AND
        (aVar1.TypeClass <> __SYSTEM.TYPE_CLASS.TYPE_LREAL OR
        aVar2.TypeClass <> __SYSTEM.TYPE_CLASS.TYPE_LREAL)) THEN
        RETURN;
    END_IF
    
    CASE (aOut.TypeClass) OF
        __SYSTEM.TYPE_CLASS.TYPE_SINT,
        __SYSTEM.TYPE_CLASS.TYPE_INT,
        __SYSTEM.TYPE_CLASS.TYPE_DINT,
        __SYSTEM.TYPE_CLASS.TYPE_LINT:
            IF (aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_SINT) THEN
                _psiVar := aVar1.pValue;
                _liVar1 := _psiVar^;
                _psiVar := aVar2.pValue;
                _liVar2 := _psiVar^;
            ELSIF (aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_INT) THEN
                _piVar := aVar1.pValue;
                _liVar1 := _piVar^;
                _piVar := aVar2.pValue;
                _liVar2 := _piVar^;
            ELSIF (aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_DINT) THEN
                _pdiVar := aVar1.pValue;
                _liVar1 := _pdiVar^;
                _pdiVar := aVar2.pValue;
                _liVar2 := _pdiVar^;
            ELSE
                SysMemCpy(pDest:=ADR(_liVar1), pSrc:=aVar1.pValue, udiCount:=aVar1.diSize);
                SysMemCpy(pDest:=ADR(_liVar2), pSrc:=aVar2.pValue, udiCount:=aVar2.diSize);
            END_IF
            _liVar2 := _liVar1*_liVar2;
            _pbOut := ADR(_liVar2);
        __SYSTEM.TYPE_CLASS.TYPE_USINT,
        __SYSTEM.TYPE_CLASS.TYPE_BYTE,
        __SYSTEM.TYPE_CLASS.TYPE_UINT,
        __SYSTEM.TYPE_CLASS.TYPE_WORD,
        __SYSTEM.TYPE_CLASS.TYPE_UDINT,
        __SYSTEM.TYPE_CLASS.TYPE_DWORD,
        __SYSTEM.TYPE_CLASS.TYPE_ULINT,
        __SYSTEM.TYPE_CLASS.TYPE_LWORD:
            SysMemCpy(pDest:=ADR(_uliVar1), pSrc:=aVar1.pValue, udiCount:=aVar1.diSize);
            SysMemCpy(pDest:=ADR(_uliVar2), pSrc:=aVar2.pValue, udiCount:=aVar2.diSize);
            _uliVar2 := _uliVar1*_uliVar2;
            _pbOut := ADR(_uliVar2);
        __SYSTEM.TYPE_CLASS.TYPE_REAL,
        __SYSTEM.TYPE_CLASS.TYPE_LREAL:
            IF (aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_REAL) THEN
                _prVar := aVar1.pValue;
                _lrVar1 := _prVar^;
                _prVar := aVar2.pValue;
                _lrVar2 := _prVar^;
            ELSE
                SysMemCpy(pDest:=ADR(_lrVar1), pSrc:=aVar1.pValue, udiCount:=aVar1.diSize);
                SysMemCpy(pDest:=ADR(_lrVar2), pSrc:=aVar2.pValue, udiCount:=aVar2.diSize);
            END_IF
            _lrVar2 := _lrVar1*_lrVar2;
            IF (aOut.TypeClass = __SYSTEM.TYPE_CLASS.TYPE_REAL) THEN
                _rResult := _lrVar2;
                _pbOut := ADR(_rResult);
            ELSE
                _pbOut := ADR(_lrVar2);
            END_IF
        ELSE
            RETURN;
    END_CASE
    
    // copy mem
    SysMemCpy(pDest:=aOut.pValue, pSrc:=_pbOut, udiCount:=aOut.diSize);
    
    Multiply := TRUE;
    

    Usage is straight forward, just do:

    VAR
        rVar1: REAL := 1.1;
        rVar2: REAL := 2.2;
        rOut: REAL;
    END_VAR
    ==========================================
    Multiply(rVar1, rVar2, rOut);
    
     
  • unzu - 2021-07-09

    Thanks for all the responses.
    Your suggestions work and are for sure a workaround - however I wonder how codesys has done it and, whether its possible to replicate for user functions.

    I pass a REAL to SQRT() and receive a REAL, I pass a LREAL to SQRT and receive a LREAL.

    It looks like there is some hidden type-deduction going on.

    Something like:

    FUNCTION SQ : TYPE_OF(x)
    VAR_INPUT
        x : ANY_REAL;
    END_VAR
    
    ==============================================
    
    SQ := x*x;
    
     

    Last edit: unzu 2021-07-09

Log in to post a comment.