This is another example of something in RPG that almost slipped by me. I now have a way not to have to define a procedure prototype in the procedures' member. All I need is the procedure, and the RPG compiler does its "magic" to do what ever it does to make this possible. This is made possible by the addition of a new control option and parameter in the procedure declaration.
The addition to the control option is a new option REQPREXP, which allows one of three values:
- *REQUIRE: All procedures are required to have a prototype (DCL-PR)
- *WARN: If a prototype is not found for a procedure a warning error, severity 10, is received when compiled
- *NO: Procedure prototype is not required for the main and exported procedures
What does this look like?
Let me start with a RPG source member that is compiled to create a module:
01 **free 02 ctl-opt nomain REQPREXP(*REQUIRE) ; 03 /copy devsrc,prototypes 04 dcl-proc Procedure1 export ; 05 dcl-pi *n char(2) ; 06 Parm1 char(2) value ; 07 end-pi ; 08 end-proc ; 09 dcl-proc Procedure2 export REQPROTO(*NO) ; 10 dcl-pi *n char(2) ; 11 Parm2 char(2) value ; 12 end-pi ; 13 end-proc ; |
Line 1: I only code in totally free RPG.
Line 2: Control options. NOMAIN means that there is no main procedure and does not use the RPG cycle. I have put the new control option in upper case. REQPREXP(*REQUIRE) tells the RPG compiler that all exported procedures must have a procedure prototype.
Line 3: I like to have my procedure protypes in an external source member that I can then copy into all the members where I need them. This source member only contains the prototype definition for Procedure1:.
**free // Prototypes dcl-pr Procedure1 char(2) ; *n char(2) value ; end-pr ; |
Lines 4 – 8: The procedure Procedure1.
Lines 9 – 13: This is Procedure2. Notice that there is no procedure prototype for it! As this procedure has the REQPROTO(*NO) a prototype is not needed. What is given at the procedure level override what is in the control option.
I compile this to a module, and add the module to the binding directory MYBDNDDIR.
What do the programs that call these procedures look like? I have two examples:
- "Traditional" cycle RPG program
- Main procedure non-cycle RPG
This is the first of those two programs:
01 **free 02 ctl-opt bnddir('MYBNDDIR') ; 03 /copy devsrc,prototypes 04 dcl-pr Procedure2 char(2) ; 05 *n char(2) value ; 06 end-pr ; 07 dcl-s Value char(2) ; 08 Value = Procedure1('XX') ; 09 Value = Procedure2('') ; 10 *inlr = *on ; |
Line 2: I am using a binding directory to hold the list of objects to bind to this program when it is compiled.
Line 3: Copy my prototypes source member into this member.
Lines 4 – 6: I have to have a procedure prototypes for Procedure2 for the RPG compiler to recognize it. If I do not I get the following:
Msg id Sv Number Seq Message text RNF7030 30 8 000800 The name or indicator PROCEDURE2 is not defined. |
Lines 8 and 9: Both of the procedures are called without error.
Next example program uses the Main procedure:
01 ctl-opt main(Main) dftactgrp(*no) bnddir('MYBNDDIR') ; 02 /copy devsrc,prototypes 03 dcl-pr Procedure2 char(2) ; 04 *n char(2) value ; 05 end-pr ; 06 dcl-proc Main ; 07 dcl-s Value char(2) ; 08 Value = Procedure1('XX') ; 09 Value = Procedure2('') ; 10 end-proc ; |
The above looks very similar to the previous "cycle" program the noteworthy differences are:
Line 1: As this is a main procedure program I need the MAIN and DFTACTGRP keywords.
Line 6: Start of the main procedure.
Line 10: End of the main procedure.
This program compiles and is called without error.
How about I want to use the REQPREXP control option to make every procedure have a prototype. The only change I need to show is:
02 ctl-opt main(Main) dftactgrp(*no) bnddir('MYBNDDIR') REQPREXP(*REQUIRE) ; 06 dcl-proc Main ; |
Line 1: I added the REQPREXP(*REQUIRE) which requires all procedures to have a prototype, including the main procedure.
As the main procedure does not have a prototype when the source member is compiled the following error is given:
6 dcl-proc Main ; ======> aaaa *RNF0203 30 a NO PROTOTYPE IS SPECIFIED FOR AN EXTERNAL PROCEDURE OR PROGRAM, AND REQPREXP(*REQUIRE) IS SPECIFIED. |
I don't want to have a prototype for the main procedure, I can just do the following:
02 ctl-opt main(Main) dftactgrp(*no) bnddir('MYBNDDIR') REQPREXP(*REQUIRE) ; 06 dcl-proc Main REQPROTO(*NO) ; |
Line 6: By adding the REQPROTO(*NO) I am telling the RPG compiler that a prototype for this procedure is not required. As I said before by adding this to any procedure supersedes the REQPREXP in the control option.
It should come as no surprise that the program compiles without error.
I am not sure how I am going to use this. Do you have ideas? If so post them in the comments, below.
You can learn more about this from the IBM website:
This article was written for IBM i 7.5 TR1 and 7.4 TR7.
Hi Simon, thanks for sharing this. As always, I enjoy consuming your articles, great work!
ReplyDeleteI think you have a typo: Line 7: End of the main procedure. S/b Line 10, yes?
For the control option and the ability to override it at the procedure level, I'm a bit confused by the use case for this. Again, you did a great job explaining how to use it, I'm just confused by what use case IBM was addressing by taking the time to implement this. Due to our business constraints, I cannot use this option yet because of the OS release(s) we're currently on. But I'm in agreement with your final statement of 'I'm not sure how I'm going to use this'. I'm interested to see if anyone posts a good use case.
Thank you for the compliment.
DeleteYes it was a typo, now corrected.
It will be interesting to see who uses this feature.
Not sure how you would use this. I would consider it bad form to omit a prototype from the copybook. And what do you gain by not having a prototype fir your main program? Just extra work if you ever call the program from RPG. This feature would only be useful if RPG had some sort of reflection and could tell from the program or module objects, what the parameters were.
ReplyDeleteNice one, Simon.
ReplyDeleteThat makes using (sub-)procedures as easy as subroutines.
No more need for those *N parameter definitions.