Question

I am getting started with RPGLE and I am trying to determine if there is any way of just defining the parameters for the procedures in my service program once. I know I can put the prototypes inside a copy member (which I have done), but then I still have to put essentially the same code into the body of the procedure (the "procedure interface").

My question is, is there some way of using the prototype to define the parameters in the procedure interface (or vice versa)?

Ideally, something like:

Prototype:

D buildForm        PR
D  formType                      10A   CONST
D  mode                           4A   CONST

Procedure:

P buildForm        B
D buildForm        PI
D  formType                            LIKE(formType)
D  mode                                LIKE(mode)

Well, ideally I would just be able to say 'use prototype' or something in the body of the procedure, or the compiler would find it on its own...


Am I misunderstanding how Prototypes and Procedure Interfaces need to be set up, or are they actually this repetitive when done correctly:

Prototype:

D buildForm        PR
D  formType                      10A   CONST
D  mode                           4A   CONST

Procedure:

P buildForm        B
D buildForm        PI
D  formType                      10A   CONST
D  mode                           4A   CONST

Thanks for reading.

Was it helpful?

Solution

On 7.1, if you are defining a sub-procedure in the same program you are using it, you don't need the prototype. You only need the prototype if you're consuming a sub-procedure from a service program or bound module (Don't bind modules to a program!)

I don't personally find the prototype burdensome to create. I copy the PI, change it to PR, put it into a /copy member and use it in many programs like this:

  /copy buck/qprotosrc,buildform
  ...
  buildform(form: mode);

The only time I need to 'double define' is in the service program where the PI is located. A way to avoid most of that is to use conditional compilation. Here's an example:

qprotosrc(buildform)

  // Build form prototype and start of interface
  // Service program will complete the interface with P    E
  /if not defined(buildform_proto)
  /define buildform_proto
 D buildForm       PR
  /else
 P buildForm       B
 D buildForm       PI
  /endif
 D  formType                     10A   CONST
 D  mode                          4A   CONST

qrpglesrc(mysrvpgm)

  /copy buck/qprotosrc,buildform
  ...
  /copy buck/qprotosrc,buildform
  // body of buildform here
  ...
   return;
 p                 e

The first time the /copy is processed, it inserts the prototype - this is what you'd want for all your consumer programs. As part of processing, it defines buildform_proto. In your service program, you'd then put a second /copy. Because buildform_proto is defined, the compiler inserts the P...B and D...PI specs. You'd have to supply the procedure body and the P...E spec.

OTHER TIPS

IMHO, this is one of those things that seems useful, but serves no real purpose and in fact could lead to other problems.

It only saves you from having to copy & paste the parm list. But it's actually more keystrokes. I'm a big fan of "Don't repeat yourself", but I don't see this as duplication but redundancy that increases error trapping. The compiler will let you know if there's a mismatch between PI and PR.

As I mention in my comment on Bucks post, you can run into problem with this. Assuming your include source has not just prototypes type other useful definitions, it's likely at some point you'll find an include file being included from multiple places. The fix for that is to simply have at the top of each include

/if defined(MYPROTO_INC)
/EOF
/endif
/define MYPROTO_INC

Now no matter how many times it's pulled in the compiler will only see the definitions once.

Given that the compile will fail if it sees the same definitions multiple times, I think it's a good idea to not intentionally include the same source. Even if by playing games we can technically not include the same symbol definitions.

Additionally, consider templating your parms..

 d formType_t      S             10a   template
 d formMode_t      s              4a   template

 D buildForm       PR
 D  type                               CONST like(formType_t)
 D  mode                               CONST like(formMode_t)

 P buildForm       B
 D buildForm       PI
 D  type                               CONST like(formType_t)
 D  mode                               CONST like(formMode_t)

This allows callers to easily create a variable to use to pass to your procedure.

You might end up with the templates in a whole other include, for example STDTYPES or APPTYPES.

Many shops solve this by putting the parms in a copy book. The copy book is then used immediately after the Prototype (PR) D-spec line, and the Procedure Interface (PI) line.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top