Wednesday, May 31, 2023

Remove the need for procedure prototypes

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:

  1. "Traditional" cycle RPG program
  2. 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.

4 comments:

  1. Hi Simon, thanks for sharing this. As always, I enjoy consuming your articles, great work!

    I 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.

    ReplyDelete
    Replies
    1. Thank you for the compliment.
      Yes it was a typo, now corrected.
      It will be interesting to see who uses this feature.

      Delete
  2. 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.

    ReplyDelete
  3. Nice one, Simon.
    That makes using (sub-)procedures as easy as subroutines.
    No more need for those *N parameter definitions.

    ReplyDelete

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.