× The internal search function is temporarily non-functional. The current search engine is no longer viable and we are researching alternatives.
As a stop gap measure, we are using Google's custom search engine service.
If you know of an easy to use, open source, search engine ... please contact support@midrange.com.



Well, I understand it quite well now. Thanks to all, especially Scott K
(though I couldn't help feeling a tad self-conscious when Scott referred
to making the process 'dummy' proof).

Arthur J. Marino
Southern Container Corporation
(631) 297 - 2276



Scott Klement <rpg400-l@xxxxxxxxxxxxxxxx>
Sent by: rpg400-l-bounces@xxxxxxxxxxxx
03/20/2008 02:51 PM
Please respond to
RPG programming on the AS400 / iSeries <rpg400-l@xxxxxxxxxxxx>


To
RPG programming on the AS400 / iSeries <rpg400-l@xxxxxxxxxxxx>
cc

Subject
Re: RPG-ILE Service Pgm and Parameter Passing






Hi Arthur,

I'm testing the proc in a service pgm that defines 3 parms, all of which

are 10 bytes, alpha and "const". The last 2 have "options(*nopass)". I'm

testing for the presence/absence of the last 2 parms using %addr(parm)
equal/not equal to *null.

Short Answer:

* You use %PARMS to check if an options(*NOPASS) parameter was passed.

* You use %ADDR(PARM)<>*NULL to check if an options(*OMIT) parameter
was passed.

Long Answer: (err.. "Long, drawn out, you'll need aspirin before it's
over answer")

Under the covers, the system is passing a list of pointers. The list
looks something like this (it's not actually RPG, but I figured you
could relate better to RPG):

D ParmList ds qualified
D opdesc *
D workarea 16A
D parm1 *
D parm2 *
D parm3 *

The structure is different on every call -- the number of fields
containing the parameters is different... So you can think of each
call as generating a different data structure in memory. The first 2
fields are always the same (though opdesc is set to *NULL if there's no
descriptor) but the parms field is different every time.

The above example shows three parameters passed by reference. If they
were passed by value, instead, the list would contain the actual data,
like this:

D ParmList ds qualified
D opdesc *
D workarea 16A
D parm1 5A
D parm2 10i 0
D parm3 *

in this case, the first two parameters are passed by value, and the 3rd
parameter is passed by reference (or is a pointer passed by value --
under the covers there's no difference between a parameter passed by
value and a pointer passed by reference).

So this is what the caller writes into memory... it creates a data
structure (dynamically/on-the-fly) and writes memory like the above.
The the procedure that was called overlays that same spot in memory with
it's own data structure (one generated from your PI...). If the data
structures don't match, you'll have problems -- that's why it's so
important to make the PR and the PI match! (PR=prototype -- that
generates the caller's structure -- PI = proc interface -- that
generates the called procedure's structure)

The first pointer (opdesc) points to a list of operational descriptors,
if an operational descriptor was passed. That's another data structure
that looks like this:

D DescList ds qualified
D parm_count 10i 0
D reserved 28A
D parm1_type 3i 0
D parm1_dtype 3i 0
D parm1_info1 3i 0
D parm1_info2 3i 0
D parm1_len 10i 0
D parm2_type 3i 0
D parm2_dtype 3i 0
D parm2_info1 3i 0
D parm2_info2 3i 0
D parm2_len 10i 0

Again, the layout of this varies depending on how many parameters are
passed. And in some languages (e.g. ILE C), you can designate whether a
descriptor is passed on a parameter-by-parameter basis... so this list
can change on the fly.

RPG *always* passes at least a "minimal" operational descriptor. Which
means that RPG *always* passes at least this much:

D DescList ds qualified
D parm_count 10i 0

If you code OPDESC on the prototype, then RPG will pass all of the other
fields in the operational descriptor as well -- but without OPDESC it
still passes that one field with the parameter count.

In any case -- this is where the value returned by %PARMS comes from...
it comes from this parm_count field in the data structure. The count
of the number of parameters passed.

So that's how parameters work. The key to understand is that these data
structures are not really RPG data structures, and aren't predefined
like an RPG data structure would be. They're generated into memory
on-the-fly. The RPG sample is just to help you understand it.

Back to your example, you have something like this:

D proc1 pr
D parm1 10a const options(*nopass)
D parm2 10a const options(*nopass)
D parm3 10a const options(*nopass)

When you call the procedure with 3 parameters, this is what the
structure under the covers looks like:

D ParmList ds qualified
D opdesc *
D workarea 16A
D parm1 *
D parm2 *
D parm3 *

OpDesc will point to a descriptor structure like this:

D DescList ds qualified
D parm_count 10i 0

None of the other fields are there, because you didn't code OPDESC. The
parm_count field will contain the number 3, because you passed 3
parameters.

Since the called routine (Proc1) is expecting the same 3 parameters,
it'll generate the exact same structure in memory, and when you check
%ADDR(PARM1), it'll give you the value that's in the "parm1" pointer of
the structure above, etc.

When you only pass 1 parameter (even though there are 3 on the
prototype, you don't use all three... you only pass one of them, as in
your example) the caller generates the following:

D ParmList ds qualified
D opdesc *
D workarea 16A
D parm1 *

It passes the same DescList structure as before, except now the
parm_count field is set to 1, because you passed only one parameter.

However, the procedure that was called (proc1) doesn't generate this
shortened data structure. It's not the one filling in the parameters...
it only knows what you have on your PI. So it generates all 3
parameters, like this:

D ParmList ds qualified
D opdesc *
D workarea 16A
D parm1 *
D parm2 *
D parm3 *

Since this is overlaying the same spot in memory that the caller
provided, parm1 will give you correct results (since in this example, it
was passed). However, parm2 and parm3 won't -- they don't really exist,
since they're overlaying memory that's beyond the caller's data
structure! Ouch. If you try to access them, you'll get whatever
happens to be in the memory AFTER the parameter list.

When you do %ADDR(PARM2), you're checking exactly that... you're
retrieving the pointer for PARM2 from the above structure. Hopefully
you understand that this'll be whatever happens to be after the
structure in memory. Hopefully you'll see why that's a problem!

In some cases, you'll get "lucky" (or "unlucky" depending on your
perspective) and the memory that follows the structure will happen to be
set to the default value of x'00' for all bytes. This will make the
pointer look like *NULL, so your %ADDR() check will work. However, in
other circumstances, as you have already discovered, you will get
something else that happens to be in that spot in memmory.

In your example, a prior call to the procedure happend to use that spot
in memory as the address of a 2nd parameter. On that subsequent call,
the spot in memory was still there and still set to that pointer, so you
managed to get the address of a variable that wasn't passed.

That's why RPG always passes the parm_count -- so you can detect the
number of parameters passed. (Though, I wish they had made this more
dummy-proof!! But that has to do with the design of the ILE
environment, and not really the design of the RPG compiler.)

By contrast, options(*OMIT) always passes all of the parameters in the
parameter list. (unless, of course, you ALSO have options(*nopass)
defined in addition to *OMIT). Instead of leaving the parameter out of
the generated structure, it still passes it, but puts *NULL in the
pointer value. That way, %ADDR()=*NULL will tell you if it was passed.

But, the flaw in that, is that *OMIT can only be used on fields passed
by reference -- since the other ones don't pass pointers and therefore
can't set the pointers to *NULL

Anyway.. this is taking way too long to explain. Hope you found it
useful or interesting. If not, just take the "short answer" and make
sure you check %PARMS (which is the same as the parm_count in my DS
examples).

As an Amazon Associate we earn from qualifying purchases.

This thread ...

Replies:

Follow On AppleNews
Return to Archive home page | Return to MIDRANGE.COM home page

This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].

Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.