Wednesday, April 11, 2018

Built in function to return name of procedure

%proc built in function to return procedure name

Until the last round of Technical Refreshes, IBM i 7.2 TR7 and 7.3 TR3, I could not find a way to retrieve the name of the procedure that I was in. The program data structure enticed me with the *PROC keyword. This was not the procedure's name, but rather either the program's or module's name.

01  DPgmDs           SDS                  qualified
02  D ProcName          *PROC

01  dcl-ds PgmDs psds qualified ;
02    ProcName *proc ;
03  end-ds ;

Fortunately these two Technical Refreshes introduced a new built in function, BiF, to returns the name of the current procedure: %PROC

This example is of what I call a closed subprocedure and how this new BiF works.

01  **free
02  ctl-opt dftactgrp(*no) ;

03  dcl-ds PgmDs psds qualified ;
04    ProcName *proc ;
05  end-ds ;

06  dcl-s ProcedureName1 char(100) ;

07  dcl-pr SomeProcedureThatDoesSomething ;
08  end-pr ;

09  ProcedureName1 = %proc() ;

10  SomeProcedureThatDoesSomething() ;

11  *inlr = *on ;


12  dcl-proc SomeProcedureThatDoesSomething ;
13    dcl-pi *n ;
14    end-pi ;

15    dcl-s ProcedureName2 char(100) ;

16    ProcedureName2 = %proc() ;
17    dump(a) ;
18  end-proc ;

Line 1: Come on, what did you expect, this example is in totally free RPG.

Line 2: As this programs contains a subprocedure I need to state that this should not run in the default activation group.

Line 3: This is the program data structure. I am only interested in the *PROC subfield so I have only defined it, and ignored the rest.

Line 6: Procedure names can be longer than ten characters I have defined this variable as 100 long.

Line 7 and 8: As this is a closed subprocedure I need to define a procedure prototype for it.

Line 9: I am retrieving the procedure name from the %PROC BiF for the first time in the main body of the program.

Line 10: Here I call the subprocedure.

Line 12: This is the start of the subprocedure, and it ends on line 18.

Lines 13 and 14: As this is a closed subprocedure it needs a procedure interface. I never name my interfaces, hence the *N. As there are not parameters passed to or returned from this subprocedure the interface is "empty".

Line 15: By defining this variable within the subprocedure it is local and not shared outside of it.

Line 16: I retrieve procedure name, from the %PROC, into the variable.

Line 17: I am using DUMP operation code to emulate what would happen if an error had occurred within the subprocedure and the message had been answered with a D.

When I look in the dump spool file, QPPGMDMP, I see the following:

NAME                  ATTRIBUTES  VALUE
PGMDS                 DS
  PROCNAME            CHAR(10)    'PGM1      '
PROCEDURENAME1        CHAR(100)   'PGM1

Local variables for subprocedure : SOMEPROCEDURETHATDOESSOMETHING 
NAME                  ATTRIBUTES  VALUE
PROCEDURENAME2        CHAR(100)   'SOMEPROCEDURETHATDOESSOMETHING

ProcName, from the program data structure, contains the name of the program, which is what I have come to expect. ProcedureName1 also contains the program's name as this variable was updated in the main body of the program. As ProcedureName2 was updated in the subprocedure it contains the name of the subprocedure.

If I use an open subprocedure %PROC returns the name of that procedure too, even when the variable is defined in the main body of the program.

01  **free
02  ctl-opt dftactgrp(*no) ;

03  dcl-ds PgmDs psds qualified ;
04    ProcName *proc ;
05  end-ds ;

06  dcl-s ProcedureName char(100) ;

07  SomeProcedureThatDoesSomething() ;

09  *inlr = *on ;


09  dcl-proc SomeProcedureThatDoesSomething ;
10    ProcedureName = %proc() ;
11    dump(a) ;
12  end-proc ;


NAME                  ATTRIBUTES   VALUE
PGMDS                 DS
  PROCNAME            CHAR(10)    'PGM2       '

Local variables for subprocedure : SOMEPROCEDURETHATDOESSOMETHING 
NAME                  ATTRIBUTES   VALUE
PROCEDURENAME         CHAR(100)   'SOMEPROCEDURETHATDOESSOMETHING

What if I have a program that uses a Main procedure?

01  **free
02  ctl-opt main(Main) ;

03  dcl-pr Main extpgm('PGM3') ;
04  end-pr ;

05  dcl-ds PgmDs psds qualified ;
06    ProcName *proc ;
07  end-ds ;

08  dcl-proc Main ;
09    dcl-pi *n ;
10    end-pi ;

11    dcl-s ProcedureName char(100) ;

12    ProcedureName = %proc() ;

13    dump(a) ;
14  end-proc ;

While ProcName contains the program name, ProcedureName contains the name of the procedure, Main. I am going to have to rethink not naming my Main procedure as I need to give it a unique name that will be returned by %PROC.

NAME                  ATTRIBUTES   VALUE
PGMDS                 DS
  PROCNAME            CHAR(10)    'PGM3      '

Local variables for subprocedure : MAIN
NAME                  ATTRIBUTES   VALUE
PROCEDURENAME         CHAR(100)   'MAIN

In this last example I have my %PROC in a procedure that is in a module.

01  **free
02  ctl-opt nomain ;

03  dcl-ds PgmDs extname('RPG4DS') psds qualified
04  end-ds ;

05  dcl-s ProcedureName char(100) ;

06  dcl-pr FirstProcedure char(1) ;
07  end-pr ;

08  dcl-proc FirstProcedure export ;
09    dcl-pi *n char(1) ;
10    end-pi ;

11    ProcedureName = %proc ;
12    PgmDs = PgmDs ;
13    dump(a) ;
14    return '1' ;
15  end-proc ;

Line 2: As this is a module, there is no main procedure.

Lines 3 and 4: In my programs I use an external definition for my program data structure. This way all the subfields are available to me when needed. The program data structure can only be defined once in the module, and must be defined outside of any procedures.

Line 5: I have defined the variable I will be using for the procedure name outside of any procedure. This way I can use it in all the procedures.

Lines 6 and 7: The procedure prototype. This procedure returns an one character value to whatever called it.

Line 8: The start of my procedure. I need the export so that the procedure will return the one character value to whatever called it.

Lines 9 and 10: As I said before I never give my procedure interfaces names. This is where I denote within the procedure that a value is being returned from it.

When I look in the print file generated by the dump I see the following information:

  1. Program data structure subfields
    1. ModulePgm (positions 334 – 343): Program containing module
    2. ModuleProc (344 – 353): Module containing procedure
    3. ProcNme (1 – 10): Module or program name
  2. ProcedureName: Name retrieved from %PROC
NAME                  ATTRIBUTES   VALUE
PGMDS                 DS
  MODULEPGM           CHAR(10)    'PGM5      '
  MODULEPROC          CHAR(10)    'MODULE1   '
  PROCNME             CHAR(10)    'MODULE1   '

PROCEDURENAME         CHAR(100)   'FIRSTPROCEDURE

This new BiF is already making me reconsider the way I name my Main procedures. I could see me using it in subprocedures and procedures for diagnostic purposes. When the operator informs me they have answered an error message with a D I can look in the dump spool file for the current procedure name in the "standard variable" I choose to use with %PROC.

 

You can learn more about this from the IBM website:

 

This article was written for IBM i 7.2 TR7 and 7.3 TR3 or later.

7 comments:

  1. Now we just need %Routine to get main or subroutine name.

    ReplyDelete
  2. FYI. You don't need to code **free in line 1 of a free format program. I never coded it in any of the dozens of free format programs I've written and they all compile with no warnings and run perfectly fine.

    ReplyDelete
    Replies
    1. If your RPG code starts in a column >= 8 then you are correct it does not, see here.

      My code starts in the first column, therefore, I need it, see here. If the **FREE is absent I get compile errors:

      RNF0257 "Form-Type entry for main procedure not valid or out of sequence."

      Delete
    2. With or Without **free
      I always add **free, just to be able to type code further than position 80.
      Regards
      Jan

      Delete
  3. I assume that line 12 in the final example is just forcing a reference to the DS? 'Cos it is a no-op.

    Also I'm interested in why you would choose to use an externally described DS rather than a /Copy for the definitions of the PSDS. It limits you to the old short names and you can't group any of the fields together etc.

    ReplyDelete
    Replies
    1. I use external data structure for this as a habit. I have at my work added ALIAS to all the fields and use the ALIAS keyword in place of the short name.

      Delete

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.