Returning an Interface from a Function

Started by Trent H., October 09, 2023, 12:02:36 PM

Previous topic - Next topic

Trent H.

I'm busy looking into implementing the new Genero Methods into an application and was wondering how to return an Interface from a Function

I have two example Types set up as

Code (genero) Select

TYPE TypeA RECORD
    notUsed STRING
END RECORD

FUNCTION (typeA TypeA) test()
    DISPLAY "Is TypeA"
END FUNCTION


And

Code (genero) Select

TYPE TypeB RECORD
    notUsed STRING
END RECORD

FUNCTION (typeB TypeB) test()
    DISPLAY "Is TypeB"
END FUNCTION


Then an Interface to use them with as

Code (genero) Select

TYPE IType INTERFACE
    test()
END INTERFACE


It seems to work fine but how can they be used in a scenario like the below where I need to return an Interface depending on an argument

Code (genero) Select

MAIN
    DEFINE typeA TypeA

    CALL typeBuilder("typeA") RETURNING typeA.*
    CALL polyTest(typeA)
END MAIN

FUNCTION typeBuilder(request STRING)
    DEFINE typeA TypeA, typeB TypeB

    IF request = "typeA" THEN
        RETURN typeA.*
    ELSE
        RETURN typeB.*
    END IF
END FUNCTION

FUNCTION polyTest(itype IType)
    CALL itype.test()
END FUNCTION


I'm trying to use in the code in MAIN as

Code (genero) Select

MAIN
    DEFINE itype IType

    CALL typeBuilder("typeA") RETURNING itype
    CALL polyTest(itype)
END MAIN

Leo S.

Hi Trent, you need to make use of the optional RETURNS clause of the FUNCTION typeBuilder
Also , since 4.1.x you don't need to return record.* anymore.
Just do this
Code (genero) Select

FUNCTION typeBuilder(request STRING) RETURNS IType
   DEFINE typeA TypeA, typeB TypeB

   IF request = "typeA" THEN
       RETURN typeA
   ELSE
       RETURN typeB
   END IF
END FUNCTION

Regards , Leo

Trent H.

Hello Leo, thanks so much for the response.

I've updated the code and it is compiling fine which is great but I am getting a "Null pointer exception" on the CALL itype.test() line in the polyTest FUNCTION.

Do I need to make any other changes for it to work with Genero 3.20? (I should have mentioned I'm not using 4.10 yet)

Leo S.

Hi Trent , I needed to fiddle a bit to make it working in 3.20,
the following code should compile and run at your side,
see the comments which explain a bit the difference to 4.x
Of course this code works also once you switch to 4.x
Code (genero) Select

TYPE TypeA RECORD
   notUsed STRING
END RECORD

TYPE TypeB RECORD
   notUsed STRING
END RECORD

TYPE IType INTERFACE
   test()
END INTERFACE

--need module scope vars for the type builder in 3.20
--and var names shouldn't be type names
--(genero is still case insensitive)
--DEFINE typeA TypeA won't work in 3.20
--also type and var definitions can't be freely mixed
--with function as its done in 4.x
DEFINE tA TypeA
DEFINE tB TypeB

FUNCTION typeBuilder(request STRING) RETURNS IType
   DEFINE it IType

   IF request = "typeA" THEN
       LET it=tA
   ELSE
       LET it=tB
   END IF
   RETURN it
END FUNCTION

FUNCTION (typeA TypeA) test()
   DISPLAY "Is TypeA"
END FUNCTION

FUNCTION (typeB TypeB) test()
   DISPLAY "Is TypeB"
END FUNCTION

Regards, Leo

Leo S.

Trent, I double checked against 4.x: also in 4.x you can't return an interface variable (which is a kinda pointer) from a variable which has function local scope.
The runtime does set it to NULL on return because the local scope of the typeBuilder was  left ...and there is no mechanism in Genero which allows to point to scopes which have been destroyed.
(It would be possible in theory by creating a temp variable and using a garbage collector).
So just use variables which have module scope for the builder and you are fine.
Regards, Leo

Trent H.

Hello Leo, oh ok I see, a bit unexpected but I think I can work with this. Thanks so much for your help! I would never have figured that out, haha