I’ve got a weird problem using RESIZE on an X-Group. Given this minimal example of my modules…
PDA SMTESTP
DEFINE DATA PARAMETER
1 SMTESTP
2 GROUP (1:*)
3 FIELD1 (A8)
3 FIELD2 (A8)
END-DEFINE
Program SMTEST
DEFINE DATA
LOCAL USING SMTESTP
END-DEFINE
*
RESET SMTESTP
*
PERFORM LOCAL-SUB
*
/* IRRELEV does not exist, but the error occurs before this line so it is never called
CALLNAT 'IRRELEV' SMTESTP.FIELD1(*)
*
INPUT (AD=IO) 'End'
*
DEFINE SUBROUTINE LOCAL-SUB
PERFORM EXTERNAL-SUB SMTESTP
END-SUBROUTINE
*
END
External subroutine EXTERNAL-SUB
DEFINE DATA
PARAMETER USING SMTESTP
END-DEFINE
*
DEFINE SUBROUTINE EXTERNAL-SUB
RESIZE ARRAY SMTESTP.GROUP TO (1:1) /* this line causes the error!
END-SUBROUTINE
*
END
…I get this error: SMTESTS 0060 NAT1255 Invalid value in ARRAY clause for REDUCE/EXPAND/RESIZE
If I modify the code in any of these three ways, the error disappears:
remove FIELD2 (which is not referenced anywhere) from SMTESTP OR
comment out this line (which is never called at runtime!): CALLNAT ‘IRRELEV’ SMTESTP.FIELD1(*) OR
call EXTERNAL-SUB directly instead of wrapping it with LOCAL-SUB
Could someone please tell me, what the problem with this code is? It looks perfectly fine to me. I debugged the hell out of it but still can’t explain what the problem is…
By default, parameters are passed by reference, but you haven’t allocated any space to SMTESTP.GROUP when you execute the PERFORM, so there is nothing to reference - a bit of a contradiction.
In SMTESTP, change the properties of FIELD1 and FIELD2 from “By Reference” to “By Value Result”. This passes the null X-array to the subroutine which passes back an expanded X-array. To verify, I added a DISPLAY and two ASSIGNs to your code.
thank you for your answer. I’ll add that to my list of possible solutions above
I understand your explanation. However, what I don’t understand is why the code works with BY REFERENCE in the PDA when changing one of the things from my list, e.g. unwrapping the external subroutine and calling it directly without the local subroutine. That should change nothing in the way the “empty” array is initialized and passed to the external subroutine. Or does it?
FIELD1 is an element of an X-group array. If you don’t change the parameter definition to BY VALUE RESULT, then pass the entire group instead of one element.
CALLNAT 'IRRELEV' SMTESTP.GROUP (*)
By removing FIELD2, you change FIELD1 from an element of an X-Group array to a standard X-array.
I can only guess that LOCAL-SUB causes the X-Group Array to be treated differently because you push EXTERNAL-SUB to a level 3 routine.
Follow Steve’s documentation link to see how X-Group Arrays are treated differently. Consider changing your structure to two X-Arrays.
DEFINE DATA PARAMETER
1 SMTESTP
2 GROUP
3 FIELD1 (A8/1:*)
3 FIELD2 (A8/1:*)
END-DEFINE
RESIZE ARRAY SMTESTP.FIELD1 TO (1:1)
RESIZE ARRAY SMTESTP.FIELD2 TO (1:1)
I have already read almost everything I could find about X-Arrays in the documentation Unfortunately, I could not find an answer to my problem there. Or am I missing something?
I could do that, but it would violate the principle of “Interface Segregation” (as I translate it to the context of Natural) because the called subprogram does not need (and should therefore not know about) the other parameters except for the given ones.
In fact, I started off doing that but grew tired of adding RESIZEs all over the place when adding a new array (which was necessary quite often during initial development). That lead to bugs because of missed or inconsistent RESIZEs/REDUCEs etc. Because of that I switched over to the X-Group.
That’s exactly what it should produce I just wanted to show that the problem remains even if the subprogram never gets called. In fact, the problem must be a compile time problem since I only need to remove the parameter SMTESTP.FIELD1(*) to make it run and not the whole line.
You mentioned “Interface Segregation”. I have always argued for Subroutine Independence (Subroutine in the generic sense, not in the Natural sense).
I view being able to change the dimensionality of an X array in a subprogram as a violation of Subroutine independence.
Consider: I have a program with an X array presently dimensioned as having ten occurrences. I issue a CALLNAT to a subprogram which changes the array to have only five occurrences. I now try to reference the seventh occurrence of the array. Boom goes my program.
Should a subroutine have the authority to destroy the ability for a program to continue? I personally think not.
Yes, I could have tested the dimensionality after the CALLNAT. I do not think I should have to do this. Forgetting about arrays for the moment, suppose I have a CALLNAT which passes three arguments, #A, #B, and #C. When my program gets control back from the subprogram, any reference to #B causes an abend. In other words, #B is gone. Is this “right”? Should I have to plan for such a possibility when writing a program to use this subprogram? I think not.
One could argue that if I am writing the program and the subprogram, and am the only person authorized to change either, I could get away with this. Logically, the program and subprogram are one logical entity (and an internal subroutine might have sufficed).
I have found that in most shops, even if things start out this way, they quickly change as circumstances dictate another program, or system, would like access to the subprogram. So much for independence, and so much for believing that one can really control such interdependence.
Sorry for the “rant”, but I have only had one cup of coffee so far this morning. Off for more.
In my example the called subprogram IRRELEV (which is replaced by a real subprogram in my case) uses the parameter BY VALUE so it cannot change its dimensions (or at least it has no consequences for the caller). But you’re right about the need to constantly check the dimensions of X-Arrays. After all the errors I got from not checking them I made a habit of doing it every time I access an array…
Wow, this is the first time I have stumbled across a real bug in Natural At least, now I know that I’m not crazy