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:
- Program data structure subfields
- ModulePgm (positions 334 – 343): Program containing module
- ModuleProc (344 – 353): Module containing procedure
- ProcNme (1 – 10): Module or program name
- 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.
Now we just need %Routine to get main or subroutine name.
ReplyDeleteNice thing to use
ReplyDeleteFYI. 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.
ReplyDeleteIf your RPG code starts in a column >= 8 then you are correct it does not, see here.
DeleteMy 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."
With or Without **free
DeleteI always add **free, just to be able to type code further than position 80.
Regards
Jan
I assume that line 12 in the final example is just forcing a reference to the DS? 'Cos it is a no-op.
ReplyDeleteAlso 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.
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.
DeleteI don't think it's ever a good idea to name the main procedure "Main". If you do that as a general rule, you can't use prototypes to call them from another program since all the prototypes would have to be called "Main". Or you'd have to do the evil thing of using prototypes in the callers that are not also copied into the programs themselves.
ReplyDeletethanks Simon, yeah that %proc bif could be handy
ReplyDelete