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



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

Follow-Ups:
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.