For Programmers: Free Programming Magazines  


Home > Archive > Cobol > July 2007 > variable length fields for flexibility in subroutines









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author variable length fields for flexibility in subroutines
Frank Swarbrick

2007-07-23, 6:55 pm

Wow! A post about COBOL!

I'm writing a new COBOL subroutine and I would like to implement an amount
of flexibility in the interface. By that I mean that I would like to allow

for, in the future, extension of the length / number of passed fields
without requiring the recompiling of all of the programs that call the
subroutine but do not care about the new field.

Generally how we've done this in the past is we simply put an unused filler
area at the end of the passed area in anticipation of using some of the
filler later. This is OK as long as the developer of the subroutine is
psychic and can anticipate the largest length that this area will ever grow
to. :-)

My thought (one of them) is to have a 'length' field as part of the passed
area, where the length specifies the length of the remainder of the area *as
specified by the caller*. The called program (callee) would not (for the
most part) work directly with the passed linkage area. Rather it would move
the passed area to working-storage, do it's work there, and then just before
returning it would move the working-storage area in to the linkage area. It
wouldn't necessarily move the entire working-storage area, though, because
that might cause overwriting of some of the calling program's
working-storage (or worse!). Rather it would only move the number of bytes
that were passed to it from the calling program.

So here's what I have. A copybook defining the linkage area between the two
programs, a subroutine (CALLEE.COB) and a main program (CALLER.COB):

'COPY.CPY':
01 LINK-AREA.
05 LINK-AREA-LEN PIC S9(4) COMP.
05 LINK-AREA-DATA.
10 LINK-FIELD-1 PIC X(8).
10 LINK-FIELD-2 PIC X(8).
10 LINK-FIELD-3 PIC X(10).
10 LINK-FIELD-4 PIC X(1).

'CALLEE.COB':
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLEE.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY 'COPY.CPY'.
LINKAGE SECTION.
01 LS-LINK-AREA.
05 LS-LINK-AREA-LEN PIC S9(4) COMP.
05 LS-LINK-AREA-DATA.
10 PIC X OCCURS 1 TO 4096
DEPENDING ON LS-LINK-AREA-LEN.
PROCEDURE DIVISION USING LS-LINK-AREA.
MOVE LS-LINK-AREA TO LINK-AREA
MOVE ALL '-' TO LINK-FIELD-4
DISPLAY 'CALLEE'
DISPLAY LINK-FIELD-4
MOVE LINK-AREA TO LS-LINK-AREA
EXIT PROGRAM.

'CALLER.COB':
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLER.
DATA DIVISION.
WORKING-STORAGE SECTION.
77 CALLEE PIC X(8) VALUE 'CALLEE '.
COPY 'COPY.CPY'.
01 MISC PIC X(120) VALUE ALL 'Q'.
PROCEDURE DIVISION.
MOVE LENGTH OF LINK-AREA-DATA TO LINK-AREA-LEN
MOVE ALL '1' TO LINK-FIELD-1
MOVE ALL '2' TO LINK-FIELD-2
MOVE ALL '3' TO LINK-FIELD-3
CALL CALLEE USING LINK-AREA
DISPLAY 'CALLER'
DISPLAY LINK-FIELD-1
DISPLAY LINK-FIELD-2
DISPLAY LINK-FIELD-3
DISPLAY LINK-FIELD-4
DISPLAY '(' MISC ')'
STOP RUN.

This does not work as I expected. When CALLEE does MOVE LINK-AREA TO
LS-LINK-AREA it ends up actually doing what would occur even if I did not
have the "OCCURS...DEPENDING ON", but instead just had "05
LS-LINK-AREA-DATA PIC X(4096)", which is that it pads the area with spaces.

I am able to make it work as I expect by changing
MOVE LINK-AREA TO LS-LINK-AREA
to
MOVE LINK-AREA-DATA TO LS-LINK-AREA-DATA

Once this is done I am free to change LINK-FIELD-4 in the copybook from 1
byte to 100 bytes. I can then recompile CALLEE but not CALLER and CALLEE
does not clobber CALLEE's MISC area (as was originally happening).

This is all using COBOL for VSE/ESA 1.1.1. Interestingly, using OpenCobol
0.33 (not the most recent pre-release, but one from some time last year) I
am able to use the original MOVE statement and it works fine.

So I have two questions:
1) Which compiler is treating the MOVE statement correctly?
2) Does this seem to be an appropriate way to handle this issue, or is there
a better idea?

No OO-COBOL solution can be implemented, as COBOL/VSE does not support it.
Additionally, COBOL/VSE also does not support the "ANY LENGTH" clause.

I suppose another solution might be to pass separate linkage areas, rather
than a 'monolithic' one, and have the first passed field be a count of the
number of remaining fields. To be honest, I don't care for either one, but
I think I'm kind of stuck.

Actually, probably the most flexible method would be to pass both the number
passed fields and the length of each field. But can you imagine what the
call would look like?

CALL CALLEE USING BY REFERENCE PARM-COUNT
BY REFERENCE PARM-1
BY CONTENT LENGTH OF PARM-1
BY REFERENCE PARM-2
BY CONTENT LENGTH OF PARM-2
BY REFERENCE PARM-3
BY CONTENT LENGTH OF PARM-3
BY REFERENCE PARM-4
BY CONTENT LENGTH OF PARM-4

It would certainly work (well, I assume it would; I haven't tried it yet).
But it's just so darn ugly! Feeling that I probably will not have to extend
the length of the fields themselves, I just can't bring myself to do it this
way.

Anyway, any thoughts are welcome. Especially from one who has implemented
or worked with something similar. I sure wish the COBOL 85 standard had
made this kind of thing simpler. I'm trying to encourage the use of generic
subroutines, but COBOL doesn't always make it easy to do so. It's hard to
build an interface that is extensible without knowing how it will be
extended in the future.

Frank

--

„„„ ÌFrank SwarbrickÌSenior Developer/Analyst „ Mainframe
ApplicationsÌFirstBank Data Corporation „ Lakewood, CO USA
Howard Brazee

2007-07-23, 6:55 pm

On Mon, 23 Jul 2007 12:20:37 -0600, "Frank Swarbrick"
<Frank.Swarbrick@efirstbank.com> wrote:

>My thought (one of them) is to have a 'length' field as part of the passed
>area, where the length specifies the length of the remainder of the area *as
>specified by the caller*. The called program (callee) would not (for the
>most part) work directly with the passed linkage area. Rather it would move
>the passed area to working-storage, do it's work there, and then just before
>returning it would move the working-storage area in to the linkage area. It
>wouldn't necessarily move the entire working-storage area, though, because
>that might cause overwriting of some of the calling program's
>working-storage (or worse!). Rather it would only move the number of bytes
>that were passed to it from the calling program.


I haven't used variable length records for decades - When I try what
you're doing, I figure my largest possible field is small enough that
I don't mind "wasting" memory.
Rick Smith

2007-07-23, 6:55 pm


"Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
news:46A49D15.6F0F.0085.0@efirstbank.com...
[snip]
Change:
> MOVE LINK-AREA TO LS-LINK-AREA

To:
MOVE LINK-AREA-LEN TO LS-LINK-AREA-LEN
MOVE LINK-AREA-DATA TO LS-LINK-AREA-DATA

Refer to the General Rules for the OCCURS clause.

When LS-LINK-AREA is used as a receiving item the full
size is used. By moving the occurence count before moving
the variably-sized data, the correct length is used. It has to
do with whether the ODO size "LS-LINK-AREA-LEN" is
part of the move.

[snip]
>
> This does not work as I expected. When CALLEE does MOVE LINK-AREA TO
> LS-LINK-AREA it ends up actually doing what would occur even if I did not
> have the "OCCURS...DEPENDING ON", but instead just had "05
> LS-LINK-AREA-DATA PIC X(4096)", which is that it pads the area with

spaces.
>
> I am able to make it work as I expect by changing
> MOVE LINK-AREA TO LS-LINK-AREA
> to
> MOVE LINK-AREA-DATA TO LS-LINK-AREA-DATA


Yes, this is because "LS-LINK-AREA-LEN" is not part
of the move; thus the length of "LS-LINK-AREA-DATA"
is calculated before the move.

> Once this is done I am free to change LINK-FIELD-4 in the copybook from 1
> byte to 100 bytes. I can then recompile CALLEE but not CALLER and CALLEE
> does not clobber CALLEE's MISC area (as was originally happening).
>
> This is all using COBOL for VSE/ESA 1.1.1. Interestingly, using OpenCobol
> 0.33 (not the most recent pre-release, but one from some time last year) I
> am able to use the original MOVE statement and it works fine.
>
> So I have two questions:
> 1) Which compiler is treating the MOVE statement correctly?


Apparently, VSE.
(See below for quote from FDIS ISO/IEC 1989:2002.)

> 2) Does this seem to be an appropriate way to handle this issue, or is

there
> a better idea?


Seems as good as any other I can conceive of in 30 minutes. <g>

[snip]
> Anyway, any thoughts are welcome. Especially from one who has implemented
> or worked with something similar. I sure wish the COBOL 85 standard had
> made this kind of thing simpler. I'm trying to encourage the use of

generic
> subroutines, but COBOL doesn't always make it easy to do so. It's hard to
> build an interface that is extensible without knowing how it will be
> extended in the future.


-----
FDIS ISO/IEC 1989:2002, page 309, 13.16.36.3
[OCCURS clause] General rules,
8) An alphanumeric group item, bit group item, national
group item, or strongly-typed group item that has an
entry subordinate to it that specifies the variable-table
format of the OCCURS clause is a variable-length data
item. When a variable-length data item is referenced, the
part of the table area used in the operation is determined
as follows:

a) If the data item referenced by data-name-1 is outside
the group, only that part of the table area that is specified
by the value of the data item referenced by data-name-1
at the start of the operation will be used. If there are no
elementary data items defined between the data description
entry of the group data item and the definition of the table
specified by format 2 and the value of the data item
referenced by data-name-1 at the start of the operation is
zero, the group data item is a zero-length item.

b) If the data item referenced by data-name-1 is included
in the same group and the group data item is referenced as
a sending operand, only that part of the table area that is
specified by the value of the data item referenced by
data-name-1 at the start of the operation will be used in the
operation. If the group is a receiving operand, the maximum
length of the group will be used.
-----



Robert Jones

2007-07-23, 6:55 pm

On Jul 23, 7:20 pm, "Frank Swarbrick" <Frank.Swarbr...@efirstbank.com>
wrote:
> Wow! A post about COBOL!
>
> I'm writing a new COBOL subroutine and I would like to implement an amount
> of flexibility in the interface. By that I mean that I would like to all=

ow
>
> for, in the future, extension of the length / number of passed fields
> without requiring the recompiling of all of the programs that call the
> subroutine but do not care about the new field.
>
> Generally how we've done this in the past is we simply put an unused fill=

er
> area at the end of the passed area in anticipation of using some of the
> filler later. This is OK as long as the developer of the subroutine is
> psychic and can anticipate the largest length that this area will ever gr=

ow
> to. :-)
>
> My thought (one of them) is to have a 'length' field as part of the passed
> area, where the length specifies the length of the remainder of the area =

*as
> specified by the caller*. The called program (callee) would not (for the
> most part) work directly with the passed linkage area. Rather it would m=

ove
> the passed area to working-storage, do it's work there, and then just bef=

ore
> returning it would move the working-storage area in to the linkage area. =

It
> wouldn't necessarily move the entire working-storage area, though, because
> that might cause overwriting of some of the calling program's
> working-storage (or worse!). Rather it would only move the number of byt=

es
> that were passed to it from the calling program.
>
> So here's what I have. A copybook defining the linkage area between the =

two
> programs, a subroutine (CALLEE.COB) and a main program (CALLER.COB):
>
> 'COPY.CPY':
> 01 LINK-AREA.
> 05 LINK-AREA-LEN PIC S9(4) COMP.
> 05 LINK-AREA-DATA.
> 10 LINK-FIELD-1 PIC X(8).
> 10 LINK-FIELD-2 PIC X(8).
> 10 LINK-FIELD-3 PIC X(10).
> 10 LINK-FIELD-4 PIC X(1).
>
> 'CALLEE.COB':
> IDENTIFICATION DIVISION.
> PROGRAM-ID. CALLEE.
> DATA DIVISION.
> WORKING-STORAGE SECTION.
> COPY 'COPY.CPY'.
> LINKAGE SECTION.
> 01 LS-LINK-AREA.
> 05 LS-LINK-AREA-LEN PIC S9(4) COMP.
> 05 LS-LINK-AREA-DATA.
> 10 PIC X OCCURS 1 TO 4096
> DEPENDING ON LS-LINK-AREA-LEN.
> PROCEDURE DIVISION USING LS-LINK-AREA.
> MOVE LS-LINK-AREA TO LINK-AREA
> MOVE ALL '-' TO LINK-FIELD-4
> DISPLAY 'CALLEE'
> DISPLAY LINK-FIELD-4
> MOVE LINK-AREA TO LS-LINK-AREA
> EXIT PROGRAM.
>
> 'CALLER.COB':
> IDENTIFICATION DIVISION.
> PROGRAM-ID. CALLER.
> DATA DIVISION.
> WORKING-STORAGE SECTION.
> 77 CALLEE PIC X(8) VALUE 'CALLEE '.
> COPY 'COPY.CPY'.
> 01 MISC PIC X(120) VALUE ALL 'Q'.
> PROCEDURE DIVISION.
> MOVE LENGTH OF LINK-AREA-DATA TO LINK-AREA-LEN
> MOVE ALL '1' TO LINK-FIELD-1
> MOVE ALL '2' TO LINK-FIELD-2
> MOVE ALL '3' TO LINK-FIELD-3
> CALL CALLEE USING LINK-AREA
> DISPLAY 'CALLER'
> DISPLAY LINK-FIELD-1
> DISPLAY LINK-FIELD-2
> DISPLAY LINK-FIELD-3
> DISPLAY LINK-FIELD-4
> DISPLAY '(' MISC ')'
> STOP RUN.
>
> This does not work as I expected. When CALLEE does MOVE LINK-AREA TO
> LS-LINK-AREA it ends up actually doing what would occur even if I did not
> have the "OCCURS...DEPENDING ON", but instead just had "05
> LS-LINK-AREA-DATA PIC X(4096)", which is that it pads the area with space=

s=2E
>
> I am able to make it work as I expect by changing
> MOVE LINK-AREA TO LS-LINK-AREA
> to
> MOVE LINK-AREA-DATA TO LS-LINK-AREA-DATA
>
> Once this is done I am free to change LINK-FIELD-4 in the copybook from 1
> byte to 100 bytes. I can then recompile CALLEE but not CALLER and CALLEE
> does not clobber CALLEE's MISC area (as was originally happening).
>
> This is all using COBOL for VSE/ESA 1.1.1. Interestingly, using OpenCobol
> 0.33 (not the most recent pre-release, but one from some time last year) I
> am able to use the original MOVE statement and it works fine.
>
> So I have two questions:
> 1) Which compiler is treating the MOVE statement correctly?
> 2) Does this seem to be an appropriate way to handle this issue, or is th=

ere
> a better idea?
>
> No OO-COBOL solution can be implemented, as COBOL/VSE does not support it.
> Additionally, COBOL/VSE also does not support the "ANY LENGTH" clause.
>
> I suppose another solution might be to pass separate linkage areas, rather
> than a 'monolithic' one, and have the first passed field be a count of the
> number of remaining fields. To be honest, I don't care for either one, b=

ut
> I think I'm kind of stuck.
>
> Actually, probably the most flexible method would be to pass both the num=

ber
> passed fields and the length of each field. But can you imagine what the
> call would look like?
>
> CALL CALLEE USING BY REFERENCE PARM-COUNT
> BY REFERENCE PARM-1
> BY CONTENT LENGTH OF PARM-1
> BY REFERENCE PARM-2
> BY CONTENT LENGTH OF PARM-2
> BY REFERENCE PARM-3
> BY CONTENT LENGTH OF PARM-3
> BY REFERENCE PARM-4
> BY CONTENT LENGTH OF PARM-4
>
> It would certainly work (well, I assume it would; I haven't tried it yet).
> But it's just so darn ugly! Feeling that I probably will not have to ext=

end
> the length of the fields themselves, I just can't bring myself to do it t=

his
> way.
>
> Anyway, any thoughts are welcome. Especially from one who has implemented
> or worked with something similar. I sure wish the COBOL 85 standard had
> made this kind of thing simpler. I'm trying to encourage the use of gene=

ric
> subroutines, but COBOL doesn't always make it easy to do so. It's hard to
> build an interface that is extensible without knowing how it will be
> extended in the future.
>
> Frank
>
> --
>
> ,,,,,, =CCFrank Swarbrick=CCSenior Developer/Analyst ,, Mainframe
> Applications=CCFirstBank Data Corporation ,, Lakewood, CO USA


I have not used DOS/VSE for a long time and it was relatively
restrictive then, however some thoughts follow:

1) If you are using call by reference, then any individual 01 linkage
definitions in the subroutine can be as large as you like (within
compiler limits) without actually occupying any storage, so you could
make them much larger than the calling program's llnkage parameters,
so long as you have a mechanism in the subroutine to prevent the
referencing of data beyond the end of the area passed by the calling
program. As you say, a length field can be used for this purpose.

2) In the 2002 standard there is an OPTIONAL parameter that can be
used with the USING phrase of the Procedure Division header, but I
douht that your compiler provides it, the z/OS V3.4 compiler
doesn't! But you might consider it as a method for the future.
Perhaps by designing your subroutine in a way that would facilitate
its use when it is available, perhaps in a few years time.

3) I think that you can have more 01 level linkage parameters in your
subroutine than are in the calling program, provided you don't
reference them. You could devise a mechanism to pass the number of
parameters in the first parameter, to tell the subroutine which ones
it can use. The correspondence between parameters in the calling and
called programs is purely sequential, (the naming of the parameters is
irrelevant for this purpose), so at least one mandatory parameter must
be first in the list to tell the subroutine how many it is allowed to
access.

Hope this helps

Robert

Frank Swarbrick

2007-07-23, 6:55 pm

Interesting! I would have never thought that the difference was that the
'depending on' data item was in the same group as the data item being
referenced by the 'depending on' data item. I just made a slight change to
move LINK-AREA-LEN to a new working-storage field called WS-LINK-AREA-LEN,
and I changed the definition of LS-LINK-AREA-DATA to depend on this new
field. And now it works as I wanted.

I guess that it kind of makes sense in that conceivably the move itself
could be setting the value of LS-LINK-AREA-LEN (although in this case the
value of LS-LINK-AREA-LEN before and after the move is the same), so COBOL
has to make as assumption of the largest possible value. I dunno.
Obviously not my first thought, but as you says, its right there in black
and white.

One additional way to make it work is this:
MOVE LINK-AREA TO LS-LINK-AREA (1:LENGTH OF LS-LINK-AREA)

Of course this is because LENGTH OF LS-LINK-AREA is calculated prior to the
move itself, so it's a known quantity.

Anyway, thanks for pointing out the verbiage. I don't think I would have
ever found it; much less interpreted it properly! :-)

Frank

n 7/23/2007 at 2:36 PM, in message <13aa4fb9pj07l2b@corp.supernews.com>,
Rick Smith<ricksmith@mfi.net> wrote:

> "Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
> news:46A49D15.6F0F.0085.0@efirstbank.com...
> [snip]
> Change:
> To:
> MOVE LINK-AREA-LEN TO LS-LINK-AREA-LEN
> MOVE LINK-AREA-DATA TO LS-LINK-AREA-DATA
>
> Refer to the General Rules for the OCCURS clause.
>
> When LS-LINK-AREA is used as a receiving item the full
> size is used. By moving the occurence count before moving
> the variably-sized data, the correct length is used. It has to
> do with whether the ODO size "LS-LINK-AREA-LEN" is
> part of the move.
>
> [snip]
not[color=darkred]
> spaces.
>
> Yes, this is because "LS-LINK-AREA-LEN" is not part
> of the move; thus the length of "LS-LINK-AREA-DATA"
> is calculated before the move.
>
1[color=darkred]
> CALLEE
> OpenCobol
[color=darkred]
> I
>
> Apparently, VSE.
> (See below for quote from FDIS ISO/IEC 1989:2002.)
>
> there
>
> Seems as good as any other I can conceive of in 30 minutes. <g>
>
> [snip]
> implemented
> generic
> to
>
> -----
> FDIS ISO/IEC 1989:2002, page 309, 13.16.36.3
> [OCCURS clause] General rules,
> 8) An alphanumeric group item, bit group item, national
> group item, or strongly-typed group item that has an
> entry subordinate to it that specifies the variable-table
> format of the OCCURS clause is a variable-length data
> item. When a variable-length data item is referenced, the
> part of the table area used in the operation is determined
> as follows:
>
> a) If the data item referenced by data-name-1 is outside
> the group, only that part of the table area that is specified
> by the value of the data item referenced by data-name-1
> at the start of the operation will be used. If there are no
> elementary data items defined between the data description
> entry of the group data item and the definition of the table
> specified by format 2 and the value of the data item
> referenced by data-name-1 at the start of the operation is
> zero, the group data item is a zero-length item.
>
> b) If the data item referenced by data-name-1 is included
> in the same group and the group data item is referenced as
> a sending operand, only that part of the table area that is
> specified by the value of the data item referenced by
> data-name-1 at the start of the operation will be used in the
> operation. If the group is a receiving operand, the maximum
> length of the group will be used.
> -----

Frank Swarbrick

2007-07-23, 6:55 pm

>>> On 7/23/2007 at 2:55 PM, in message
<1185224121.375977.216160@r34g2000hsd.googlegroups.com>, Robert
Jones<rjones0@hotmail.com> wrote:
> I have not used DOS/VSE for a long time and it was relatively
> restrictive then, however some thoughts follow:
>
> 1) If you are using call by reference, then any individual 01 linkage
> definitions in the subroutine can be as large as you like (within
> compiler limits) without actually occupying any storage, so you could
> make them much larger than the calling program's llnkage parameters,
> so long as you have a mechanism in the subroutine to prevent the
> referencing of data beyond the end of the area passed by the calling
> program. As you say, a length field can be used for this purpose.


Yep, and this is exactly what I'm looking to do. I'm just annoyed that I
have to explicitly pass the length. As you might have noticed, I declared
the linkage as being up to 4098 bytes, but I was passing only 29 bytes.

> 2) In the 2002 standard there is an OPTIONAL parameter that can be
> used with the USING phrase of the Procedure Division header, but I
> douht that your compiler provides it, the z/OS V3.4 compiler
> doesn't! But you might consider it as a method for the future.
> Perhaps by designing your subroutine in a way that would facilitate
> its use when it is available, perhaps in a few years time.


Yeah, OPTIONAL would be a nice feature. I won't hold by breath waiting for
it, though, as I would certainly die first. :-)

> 3) I think that you can have more 01 level linkage parameters in your
> subroutine than are in the calling program, provided you don't
> reference them. You could devise a mechanism to pass the number of
> parameters in the first parameter, to tell the subroutine which ones
> it can use. The correspondence between parameters in the calling and
> called programs is purely sequential, (the naming of the parameters is
> irrelevant for this purpose), so at least one mandatory parameter must
> be first in the list to tell the subroutine how many it is allowed to
> access.


Yep. This was one of the other methods I mentioned. Which is where
OPTIONAL would come in handle, rather then having the caller explicitly pass
the number of parameters.

Thanks,
Frank
HeyBub

2007-07-23, 6:55 pm

Frank Swarbrick wrote:
> <1185224121.375977.216160@r34g2000hsd.googlegroups.com>, Robert
> Jones<rjones0@hotmail.com> wrote:
>
> Yep, and this is exactly what I'm looking to do. I'm just annoyed
> that I have to explicitly pass the length. As you might have
> noticed, I declared the linkage as being up to 4098 bytes, but I was
> passing only 29 bytes.


You don't have to pass the length if you pass some delimiter. The subroutine
can calculate the length by scanning for the delimiter.



Pete Dashwood

2007-07-24, 3:55 am


"Howard Brazee" <howard@brazee.net> wrote in message
news:lb1aa353v90v36trefhjmf55caags7hc03@
4ax.com...
> On Mon, 23 Jul 2007 12:20:37 -0600, "Frank Swarbrick"
> <Frank.Swarbrick@efirstbank.com> wrote:
>
>
> I haven't used variable length records for decades - When I try what
> you're doing, I figure my largest possible field is small enough that
> I don't mind "wasting" memory.


Yep, fixed length works for me too. I use an "Interface Block" that is a
single 01 level.

It usually contains a return code, message, and any other parameters I want
to pass, as subordinate levels.

Mine are normally 8K but that is just arbritrary, for compatiblity with COM
BSTRINGs which must be 8K when handled by Fujitsu NetCOBOL.

I wouldn't touch OCCURS...DEPENDING with very long tongs and rubber gloves,
especially not in the Linkage section...

Pete.


Frank Swarbrick

2007-07-24, 6:55 pm

>>> On 7/23/2007 at 9:22 PM, in message
<5gl9kaF3h1mj2U1@mid.individual.net>,
Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:

> "Howard Brazee" <howard@brazee.net> wrote in message
> news:lb1aa353v90v36trefhjmf55caags7hc03@
4ax.com...
> passed
[color=darkred]
the[color=darkred]
[color=darkred]
because[color=darkred]
>
> Yep, fixed length works for me too. I use an "Interface Block" that is a
>
> single 01 level.
>
> It usually contains a return code, message, and any other parameters I
> want
> to pass, as subordinate levels.
>
> Mine are normally 8K but that is just arbritrary, for compatiblity with
> COM
> BSTRINGs which must be 8K when handled by Fujitsu NetCOBOL.
>
> I wouldn't touch OCCURS...DEPENDING with very long tongs and rubber
> gloves,
> especially not in the Linkage section...


I don't suppose you'd mind posting the format of this "Interface Block"...?

Thanks,
Frank

Pete Dashwood

2007-07-24, 9:55 pm


"Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
news:46A5CA8C.6F0F.0085.0@efirstbank.com...
> <5gl9kaF3h1mj2U1@mid.individual.net>,
> Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:
>
<snip>
[color=darkred]
>
> I don't suppose you'd mind posting the format of this "Interface
> Block"...?
>

Sure, no problem.

Let's just clear the decks, first, though... :-)

<DISCLAIMER>
The following post encompasses some controversial viewpoints. There are pros
and cons and the views expressed are arguable. However, I really don't care
whether people adopt what is here or not, so I won't be arguing it. What is
here works for me and has done for a considerable time.
If you can add anything positive, I'd really love to hear from you;
(especially with regard to translating COBOL data definitions into C#... I
don't pretend to expertise in this area, although I'm trying to acquire it).
There is C# code posted here as well as COBOL. Apparently that's offensive
to some here. I suggest you get over it, or skip this thread.
</DISCLAIMER>

What is an "Interface Block"?

This is just a structured block which uses COBOL's almost unique ability for
referencing fixed length fields. I've used them for years and they can be
extended easily and managed without trouble. Mine are usally 8K because it
just so happens that this is the length required to use the COM BSTRING
interface under Fujitsu OO COBOL, but they could be any size you like,
really. Pick something that is machine friendly...4,8, 16K

I usually start them off with a return code and a message, then the
parameters I'm interested in. Some of mine include an "action code" as well,
so they can do different things (e.g. the Interface Block to an access
module might specify an action code of "INSERT", "SELECT","UPDATE", or
"DELETE", along with a common buffer area, and any other parameters.

"Generalizing" parameterization is a very desirable thing, because the fewer
changes you make to interfaces, the fewer problems you are likely to
encounter later. I like parameters that are just...

LINKAGE SECTION.
01 passed-data pic x(8192).

then, before calling

....move progname-interface-block to passed data
call progname
using passed-data
....

Upon receiving...

move passed-data to ws-interface-block

Before returning...

move ws-interface-block to passed-data
....

Where progname-interface-block and ws-interface-block are the same COPYbook.

Now, yes, this does mean you have two copies of the interface block (one in
the calling program, and one in the called one) rather than just a single
shared copy as you would through linkage. For some people, this would not be
acceptable, but I find there are actually some benefits to it. (Interactive
debugging is greatly simplified for a start, but many people here apparently
dont use interactive debugging anyway, so for them it is academic. )

Another reason why this doesn't bother me is because the interface block is
usually much smaller than the full 8K allocated, so it isn't as wasteful as
you might think.

The major benefit is that you can change the interface block and extend it,
WITHOUT modifying the program linkage or interfacing.

This is particularly useful when dealing with nested COBOL programs.
(Fujitsu OO COBOL implements Class Methods through nested sub-programming)

(You need to recompile all programs using the block so they all have the
latest COPY, but you'd need to do that anyway if you did it through
Linkage.)

Here's an example:

000230 01 ws-interfaceBlock.
000240 12 ws-return pic x(5).
000250 88 ws-OK value '00000'. *> will contain SQLSTATE
if
000260 *> there
is a DB error
000270 12 ws-message pic x(256). *> will contain SQLMSG if
000280 *> there is a DB
error
000290 12 ws-buffer pic x(2048). *> holds free format address data
000300 *> this will be
formatted on return
000310 12 ws-breakdown.
000320 15 ws-streetNo pic x(20).
000330 15 ws-POBoxNo pic x(20).
000340 15 ws-RDNo pic x(8).
000350 15 ws-street pic x(150).
000360 15 ws-locality pic x(150).
000370 15 ws-city pic x(50).
000380 15 ws-lobby pic x(150).
000390 15 ws-postCode pic x(4).
000400 15 ws-addressType pic x(1).
000410 15 ws-streetSDX pic x(4).
000420 15 ws-localitySDX pic x(4).
000430 15 ws-lobbySDX pic x(4).
000440 15 ws-prologue pic x(100).
000450 12 ws-interface pic x.
000460 88 free-format-input value '1'.
000470 88 fixed-field-input value '2'.
000480 88 XML-input value '3'.
000490 12 ws-streetMatchFlag pic x(1).
000500 88 street-fuzzy value '0'.
000510 88 street-exact value '1'.
000520 12 ws-localityMatchFlag pic x(1).
000530 88 locality-fuzzy value '0'.
000540 88 locality-exact value '1'.
000550 12 ws-repeatLocalityFlag pic x(1).
000560 88 no-Locality value '1'.
000570 88 repeatLocality value '0'. *> used if
Locality = City
000580 12 ws-ignoreInvalidPostcodeFlag pic x(1).
000590 88 ignoreInvalidPostCode value '1'.
000600 88 reportInvalidPostCode value '0'. *>stops if Post
Code is invalid
000610 12 ws-foreignFlag pic x(1).
000620 88 foreign-address value '1'. *>stops if foreign
address detected
000630 88 NOT-foreign-address value '0'.
000631 12 ws-user-logon pic x(50).


Note there is no filler at the end.

To change the length of any of the fields, or to add new ones, is dead easy
in COBOL (it can become more problematic in other languages, see below)

Change the block, amend the affected programs that use the new fields,
recompile all programs using the new block... done. Exactly the same process
as you would use if you had the block defined in Linkage.

No messing with fillers, no counting bytes and setting the count into length
parameters (then finding it was wrong :-)).

It is straightforward and simple. Why mess with OCCURS ...DEPENDING...?

Here's where it gets a bit sticky...

I have programs written in C# that I want to use this same interface. I
thought about this for some time and tried to come up with a consistent way
I could manage this block in C# (the long term aim is to generate it
automatically from the COBOL definition, but I haven't done that yet.
Obviously, for any kind of generation to be possible, the C# implementation
needs to address the fields consistently...)

I need to be able to address each field in the block, and I need to be able
to address the block as a whole. I also need to be easily able to modify or
add fields to it.

(The hierarchic data definition of COBOL facilitates all of this, but it is
arguably more "difficult" in languages that don't define data that way.)

I don't know if the solution I finally arrived at is good, bad, or
indifferent, but it certainly works... :-) (Things with that attribute
usually get my vote...:-))

Every field is a property of the Class and can be accessed using GET and SET
methods.

An alternative I considered (and discarded) was to have an array with the
offset and length of every field, then dynamically build a SUBSTRING
statement every time I needed to access a field. This seemed too unwieldy.
The solution below gets compiled into a .DLL and so the source code is not
visible in any applications that use the interface; they just have a
reference to the Assembly. (Another example of Source code being
unimportant... :-))

Here's the C# Class that implements the above block...

using System;
using System.Collections.Generic;
using System.Text;

namespace AVSInterfaceBlockBuilder
{
public class AVSInterfaceBlockBuilder
{

private string _IntBlock;
// A read-write instance property:
public string IB
{
get
{
return _IntBlock;
}
set
{
_IntBlock = value;
_IntBlock = _IntBlock.PadRight(8192);
//reset all the subfields as the group field has been
changed...
_IBreturn = _IntBlock.Substring(0, 5);

_IBmessage = _IntBlock.Substring(_IBreturn.Length, 256);

_IBbuffer = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length,
2048);

_IBstreetNo = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length,
20);

_IBPOBoxNo = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length,
20);

_IBRDNo = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length,
8);

_IBstreet = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length,
150);

_IBlocality = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length,
150);

_IBcity = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length,
50);

_IBlobby = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length,
150);


_IBpostCode = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length,
4);

_IBaddressType = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length,
1);

_IBstreetSDX = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length,
4);

_IBlocalitySDX = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length,
4);

_IBlobbySDX = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length,
4);

_IBprologue = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length,
100);

_IBinterface = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length,
1);

_IBstreetMatchFlag = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length,
1);

_IBlocalityMatchFlag = _IntBlock.Substring(_IBreturn.Length
+
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length +
_IBstreetMatchFlag.Length,
1);

_IBrepeatLocalityFlag = _IntBlock.Substring(_IBreturn.Length
+
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length +
_IBstreetMatchFlag.Length
+
_IBlocalityMatchFlag.Length,
1);

_IBignoreInvalidPostcodeFlag =
_IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length +
_IBstreetMatchFlag.Length
+
_IBlocalityMatchFlag.Length
+
_IBrepeatLocalityFlag.Length,
1);

_IBforeignFlag = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length +
_IBstreetMatchFlag.Length
+
_IBlocalityMatchFlag.Length
+
_IBrepeatLocalityFlag.Length
+
_IBignoreInvalidPostcodeFlag.Length,
1);

_IBLogonID = _IntBlock.Substring(_IBreturn.Length +
_IBmessage.Length +
_IBbuffer.Length +
_IBstreetNo.Length +
_IBPOBoxNo.Length +
_IBRDNo.Length +
_IBstreet.Length +
_IBlocality.Length +
_IBcity.Length +
_IBlobby.Length +
_IBpostCode.Length +
_IBaddressType.Length +
_IBstreetSDX.Length +
_IBlocalitySDX.Length +
_IBlobbySDX.Length +
_IBprologue.Length +
_IBinterface.Length +
_IBstreetMatchFlag.Length
+
_IBlocalityMatchFlag.Length
+
_IBrepeatLocalityFlag.Length
+
_IBignoreInvalidPostcodeFlag.Length
+
_IBforeignFlag.Length,
50);
}
}
private string _IBreturn;
// A read-write instance property:
public string IBreturn
{
get
{
return _IBreturn;
}
set
{
_IBreturn = value;
_IBreturn = _IBreturn.PadRight(5, char.Parse("0"));
_IntBlock = _IBreturn +
_IntBlock.Substring(_IBreturn.Length, _IntBlock.Length - 5);
}
}


private string _IBmessage;
// A read-write instance property:
public string IBmessage
{
get
{
return _IBmessage;
}
set
{
_IBmessage = value;
_IBmessage = _IBmessage.PadRight(256);
_IntBlock = _IntBlock.Substring(0, 5) + _IBmessage +
_IntBlock.Substring(261, _IntBlock.Length - 261);
}
}

private string _IBbuffer;
// A read-write instance property:
public string IBbuffer
{
get
{
return _IBbuffer;
}
set
{
_IBbuffer = value;
_IBbuffer = _IBbuffer.PadRight(2048);
_IntBlock = _IntBlock.Substring(0, 261) + _IBbuffer +
_IntBlock.Substring(2309, _IntBlock.Length - 2309);
}
}
private string _IBstreetNo;
// A read-write instance property:
public string IBstreetNo
{
get
{
return _IBstreetNo;
}
set
{
_IBstreetNo = value;
_IBstreetNo = _IBstreetNo.PadRight(20);
_IntBlock = _IntBlock.Substring(0, 2309) + _IBstreetNo +
_IntBlock.Substring(2329, _IntBlock.Length - 2329);
}
}
private string _IBPOBoxNo;
// A read-write instance property:
public string IBPOBox
{
get
{
return _IBPOBoxNo;
}
set
{
_IBPOBoxNo = value;
_IBPOBoxNo = _IBPOBoxNo.PadRight(20);
_IntBlock = _IntBlock.Substring(0, 2329) + _IBPOBoxNo +
_IntBlock.Substring(2349, _IntBlock.Length - 2349);
}
}

private string _IBRDNo;
// A read-write instance property:
public string IBRDNo
{
get
{
return _IBRDNo;
}
set
{
_IBRDNo = value;
_IBRDNo = _IBRDNo.PadRight(8);
_IntBlock = _IntBlock.Substring(0, 2349) + _IBRDNo +
_IntBlock.Substring(2357, _IntBlock.Length - 2357);
}
}

private string _IBstreet;
// A read-write instance property:
public string IBstreet
{
get
{
return _IBstreet;
}
set
{
_IBstreet = value;
_IBstreet = _IBstreet.PadRight(150);
_IntBlock = _IntBlock.Substring(0, 2357) + _IBstreet +
_IntBlock.Substring(2507, _IntBlock.Length - 2507);
}
}

private string _IBlocality;
// A read-write instance property:
public string IBlocality
{
get
{
return _IBlocality;
}
set
{
_IBlocality = value;
_IBlocality = _IBlocality.PadRight(150);
_IntBlock = _IntBlock.Substring(0, 2507) + _IBlocality +
_IntBlock.Substring(2657, _IntBlock.Length - 2657);
}
}

private string _IBcity;
// A read-write instance property:
public string IBcity
{
get
{
return _IBcity;
}
set
{
_IBcity = value;
_IBcity = _IBcity.PadRight(50);
_IntBlock = _IntBlock.Substring(0, 2657) + _IBcity +
_IntBlock.Substring(2707, _IntBlock.Length - 2707);
}
}

private string _IBlobby;
// A read-write instance property:
public string IBlobby
{
get
{
return _IBlobby;
}
set
{
_IBlobby = value;
_IBlobby = _IBlobby.PadRight(150);
_IntBlock = _IntBlock.Substring(0, 2707) + _IBlobby +
_IntBlock.Substring(2857, _IntBlock.Length - 2857);
}
}

private string _IBpostCode;
// A read-write instance property:
public string IBpostCode
{
get
{
return _IBpostCode;
}
set
{
_IBpostCode = value;
_IBpostCode = _IBpostCode.PadRight(4);
_IntBlock = _IntBlock.Substring(0, 2857) + _IBpostCode +
_IntBlock.Substring(2861, _IntBlock.Length - 2861);
}
}

private string _IBaddressType;
// A read-write instance property:
public string IBaddressType
{
get
{
return _IBaddressType;
}
set
{
_IBaddressType = value;
_IBaddressType = _IBaddressType.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2861) + _IBaddressType +
_IntBlock.Substring(2862, _IntBlock.Length - 2862);
}
}

private string _IBstreetSDX;
// A read-write instance property:
public string IBstreetSDX
{
get
{
return _IBstreetSDX;
}
set
{
_IBstreetSDX = value;
_IBstreetSDX = _IBstreetSDX.PadRight(4);
_IntBlock = _IntBlock.Substring(0, 2862) + _IBstreetSDX +
_IntBlock.Substring(2866, _IntBlock.Length - 2866);
}
}

private string _IBlocalitySDX;
// A read-write instance property:
public string IBlocalitySDX
{
get
{
return _IBlocalitySDX;
}
set
{
_IBlocalitySDX = value;
_IBlocalitySDX = _IBlocalitySDX.PadRight(4);
_IntBlock = _IntBlock.Substring(0, 2866) + _IBlocalitySDX +
_IntBlock.Substring(2870, _IntBlock.Length - 2870);
}
}

private string _IBlobbySDX;
// A read-write instance property:
public string IBlobbySDX
{
get
{
return _IBlobbySDX;
}
set
{
_IBlobbySDX = value;
_IBlobbySDX = _IBlobbySDX.PadRight(4);
_IntBlock = _IntBlock.Substring(0, 2870) + _IBlobbySDX +
_IntBlock.Substring(2874, _IntBlock.Length - 2874);
}
}

private string _IBprologue;
// A read-write instance property:
public string IBprologue
{
get
{
return _IBprologue;
}
set
{
_IBprologue = value;
_IBprologue = _IBprologue.PadRight(100);
_IntBlock = _IntBlock.Substring(0, 2874) + _IBprologue +
_IntBlock.Substring(2974, _IntBlock.Length - 2974);
}
}

private string _IBinterface;
// A read-write instance property:
public string IBinterface
{
get
{
return _IBinterface;
}
set
{
_IBinterface = value;
_IBinterface = _IBinterface.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2974) + _IBinterface +
_IntBlock.Substring(2975, _IntBlock.Length - 2975);
}
}

private string _IBstreetMatchFlag;
// A read-write instance property:
public string IBstreetMatchFlag
{
get
{
return _IBstreetMatchFlag;
}
set
{
_IBstreetMatchFlag = value;
_IBstreetMatchFlag = _IBstreetMatchFlag.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2975) +
_IBstreetMatchFlag + _IntBlock.Substring(2976, _IntBlock.Length - 2976);
}
}

private string _IBlocalityMatchFlag;
// A read-write instance property:
public string IBlocalityMatchFlag
{
get
{
return _IBlocalityMatchFlag;
}
set
{
_IBlocalityMatchFlag = value;
_IBlocalityMatchFlag = _IBlocalityMatchFlag.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2976) +
_IBlocalityMatchFlag + _IntBlock.Substring(2977, _IntBlock.Length - 2977);
}
}

private string _IBrepeatLocalityFlag;
// A read-write instance property:
public string IBrepeatLocalityFlag
{
get
{
return _IBrepeatLocalityFlag;
}
set
{
_IBrepeatLocalityFlag = value;
_IBrepeatLocalityFlag = _IBrepeatLocalityFlag.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2977) +
_IBrepeatLocalityFlag + _IntBlock.Substring(2978, _IntBlock.Length - 2978);
}
}

private string _IBignoreInvalidPostcodeFlag;
// A read-write instance property:
public string IBignoreInvalidPostcodeFlag
{
get
{
return _IBignoreInvalidPostcodeFlag;
}
set
{
_IBignoreInvalidPostcodeFlag = value;
_IBignoreInvalidPostcodeFlag =
_IBignoreInvalidPostcodeFlag.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2978) +
_IBignoreInvalidPostcodeFlag + _IntBlock.Substring(2979, _IntBlock.Length -
2979);
}
}

private string _IBforeignFlag;
// A read-write instance property:
public string IBforeignFlag
{
get
{
return _IBforeignFlag;
}
set
{
_IBforeignFlag = value;
_IBforeignFlag = _IBforeignFlag.PadRight(1);
_IntBlock = _IntBlock.Substring(0, 2979) + _IBforeignFlag +
_IntBlock.Substring(2980, _IntBlock.Length - 2980);
}
}
private string _IBLogonID;
// A read-write instance property:
public string IBLogonID
{
get
{
return _IBLogonID;
}
set
{
_IBLogonID = value;
_IBLogonID = _IBLogonID.PadRight(50);
_IntBlock = _IntBlock.Substring(0, 2980) + _IBLogonID +
_IntBlock.Substring(3030, _IntBlock.Length - 3030);
}
}
public void init()
{
IB = " ";
}



}
}


The whole rigmarole above is necessitated by C# not being able to modify a
string. Once you create a string it is inviolate. You can slice and dice it,
add to it, but to change it you must create a new copy of it. (There are
some very sound reasons as to why it behaves this way, but we won't go into
them here... )

As you can see, this is pretty huge and tiresome, compared to the COBOL
definition of the same block.

But it has some benefits that the COBOL definition doesn't.

The C# Class protects the fields by making sure they cannot be inadvertently
modified, and making them accessible only by GET and SET. (If I wanted to do
any particular validation, I could build it into the SET method of the
individual field; I wouldn't have to re-compile any programs using this
..DLL. In effect, this is now an "interface object". It is encapsulated and
isolated just like it should be. It also includes a method to initialize the
whole block, so programs are relieved of this. They just instantiate the
object and invoke its "init" method...
[
// get an Interface Block object instance...
AVSInterfaceBlockBuilder.AVSInterfaceBlockBuilder objIntBlock = new
AVSInterfaceBlockBuilder.AVSInterfaceBlockBuilder();
// initialize the interface block
objIntBlock.init();
// set the street...
objIntBlock.IBstreet = "Primrose Lane" //(cf. MOVE "Primrose Lane" TO
IBStreet of objIntBlock... not so different, really)
....
etc
]
Because I eventually intend to generate these blocks (it is pretty tedious
coding them by hand :-)) I have taken care to ensure it is 8192 bytes, and
each field entry is reasonably consistent. Smart generation software can pad
it to any size I want, so if the block exceeded 8K it could automatically be
padded to 12K or 16K and a warning issued...

COBOL is excellent for handling fixed length fields and that is why I would
advocate the avoidance of variable length processing in it.

But at a broader level, the encapsulation of parameters into Objects is
simply superior. It is VERY easy to modify or extend parameters when they
are NOT included (directly) in the programs that consume them.

(Whether or not you SHOULD maintain these Classes is a matter for a
different debate. Usually, as in this case, they are not so trivial. Auto
generation seems like the best option, at the moment, to me... Maybe, the
source is kept as COBOL (which is easy to amend, and designed as a "source
oriented" language) and THAT gets updated, then the generator generates a
new .dll for the C# programs that access it. Eventually, I'll just build a
graphic interface to the generator and bypass the COBOL altogether...))

Pete.


LX-i

2007-07-25, 3:55 am

Pete Dashwood wrote:
> An alternative I considered (and discarded) was to have an array with the
> offset and length of every field, then dynamically build a SUBSTRING
> statement every time I needed to access a field. This seemed too unwieldy.
> The solution below gets compiled into a .DLL and so the source code is not
> visible in any applications that use the interface; they just have a
> reference to the Assembly. (Another example of Source code being
> unimportant... :-))


What you might look into is creating a description of your "interface
block" in XML, then parsing that document and spinning through the nodes
to build your interface block. You'd still have to modify (or extend)
the class if you added a new property, but you could have a generic
"Generate Block" getter that assembled it according to the specs in the
file.

Something like...

<interface_block size="8192">
<parameter name="street" position="1" size="150" />
<parameter name="city" position="2" size="50" />
...
</interface_block>

(The position attribute is just so you can retrieve them in the right
order, even if they're not in the right order in the XML file.) Judging
from the way you've talked about your components, having a reusable
configuration for interface blocks might be nice.

--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ / \/ _ o ~ Live from Albuquerque, NM! ~
~ _ /\ | ~ ~
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
~ Business E-mail ~ daniel @ "Business Website" below ~
~ Business Website ~ http://www.djs-consulting.com ~
~ Tech Blog ~ http://www.djs-consulting.com/linux/blog ~
~ Personal E-mail ~ "Personal Blog" as e-mail address ~
~ Personal Blog ~ http://daniel.summershome.org ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~

GEEKCODE 3.12 GCS/IT d s-:+ a C++ L++ E--- W++ N++ o? K- w$ !O M--
V PS+ PE++ Y? !PGP t+ 5? X+ R* tv b+ DI++ D+ G- e h---- r+++ z++++

"Who is more irrational? A man who believes in a God he doesn't see,
or a man who's offended by a God he doesn't believe in?" - Brad Stine
Alistair

2007-07-25, 7:55 am


Pete Dashwood wrote:
>
> I wouldn't touch OCCURS...DEPENDING with very long tongs and rubber gloves,
> especially not in the Linkage section...
>
> Pete.


I love OCCURS DEPENDING ON. Fits in nicely with PERFORM VARYING, etc.
I can only guess that you had an unfortunate experience in your
formative years (in IT) with ODO.

Pete Dashwood

2007-07-25, 7:55 am

Thanks Daniel,

I hadn't really considered using XML but it is certainly feasible

Quick comments below


"LX-i" <lxi0007@netscape.net> wrote in message
news:Gr6dnQRo2Y4ZUjvbnZ2dnUVZ_u6rnZ2d@co
mcast.com...
> Pete Dashwood wrote:
>
> What you might look into is creating a description of your "interface
> block" in XML, then parsing that document and spinning through the nodes
> to build your interface block. You'd still have to modify (or extend) the
> class if you added a new property, but you could have a generic "Generate
> Block" getter that assembled it according to the specs in the file.


It's funny, this is almost exactly what I do for the "remembering" of
mapping profiles (where columns on a user DB are mapped against the things
my engine is "interested" in...) and other global parameters that are used
in the Desktop implementation. It works very well. I just didn't make the
connection to extending it to something as "internal" as this.

>
> Something like...
>
> <interface_block size="8192">
> <parameter name="street" position="1" size="150" />
> <parameter name="city" position="2" size="50" />
> ...
> </interface_block>
>
> (The position attribute is just so you can retrieve them in the right
> order, even if they're not in the right order in the XML file.) Judging
> from the way you've talked about your components, having a reusable
> configuration for interface blocks might be nice.


It's a very good idea. I want to consider it further and will post back when
I've had time to think it through.

Thanks

Pete
--
"I used to write COBOL...now I can do anything."


Pete Dashwood

2007-07-25, 7:55 am



"Alistair" <alistair@ld50macca.demon.co.uk> wrote in message
news:1185360708.315094.272660@57g2000hsv.googlegroups.com...
>
> Pete Dashwood wrote:
>
> I love OCCURS DEPENDING ON. Fits in nicely with PERFORM VARYING, etc.
> I can only guess that you had an unfortunate experience in your
> formative years (in IT) with ODO.
>

No Doctor Freud, not at all...:-)

I realise there will be much disagreement on this and it is really academic
as far as I'm concerned. I'm moving away from COBOL so I can't get
passionate about it.

Certainly in the early days, the overheads were much more important.

Nevertheless, in a career spanning 40 years, I have never coded this
construct from choice. I have dealt with it when doing maintenance and when
it is a pre-existing part of live code, but you won't find anything written
by me, that I had any control over, which contains this ghastly construct.

I can summarize my objections as follows:

1. It is just...ugly.
2. It requires a control field to be set before you can do anything with it.
3. It doesn't deliver what it promises (that is always a show-stopper for me
:-)). It makes you think it is saving you memory, but it still allocates the
maximum, so what exactly is the point?
4. It behaves differently across different platforms, depending on what
compile options you set. (So much for transportability...)
5. It introduces an unnecessary overhead. (that doesn't bother me so much; I
realise modern optimizing compilers can minimise this but, at the end of the
day, it is going to generate indirect addressing through registers on most
machines, when many of them have perfectly good hardware for moving stuff
directly.)
6. It requires more writing in source. Anything (apart from pertinent
documentation and formatting) that makes the source code bigger gets black
marks from me.
7. It is a frequent source of errors and crashes.(The control field gets set
incorrectly for any number of reasons, or it doesn't get set at all for any
number of reasons. These can be data related, and very hard to pick up
during development testing. Live data comes along and once every couple of
years it crashes.
8. Did I mention it was ugly?

OK, that's enough time wasted on something that is a waste of time in
itself...

If you or others LOVE it, great. I wish you happiness :-)

And there is NO connection between PERFORM...VARYING (which is an incredibly
useful construct, invaluable, in fact) and OCCURS...DEPENDING ON (which
isn't...)

Pete.
--
"I used to write COBOL...now I can do anything."


Frank Swarbrick

2007-07-25, 6:55 pm

>>> On 7/24/2007 at 8:34 PM, in message
<5gnr6mF3hba38U1@mid.individual.net>,
Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:

> "Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
> news:46A5CA8C.6F0F.0085.0@efirstbank.com...
> <snip>
>
a[color=darkred]
> Sure, no problem.
>
> Let's just clear the decks, first, though... :-)
>
> <DISCLAIMER>
> The following post encompasses some controversial viewpoints. There are
> pros
> and cons and the views expressed are arguable. However, I really don't
> care
> whether people adopt what is here or not, so I won't be arguing it. What
> is
> here works for me and has done for a considerable time.
> If you can add anything positive, I'd really love to hear from you;
> (especially with regard to translating COBOL data definitions into C#...
> I
> don't pretend to expertise in this area, although I'm trying to acquire
> it).
> There is C# code posted here as well as COBOL. Apparently that's
> offensive
> to some here. I suggest you get over it, or skip this thread.
> </DISCLAIMER>


Well I, for one, don't find posting C# code here (or code for any other
language for that matter). Knowing only one language is a very limiting
thing indeed! :-)

> What is an "Interface Block"?
>
> This is just a structured block which uses COBOL's almost unique ability
> for
> referencing fixed length fields. I've used them for years and they can
> be
> extended easily and managed without trouble. Mine are usally 8K because
> it
> just so happens that this is the length required to use the COM BSTRING
> interface under Fujitsu OO COBOL, but they could be any size you like,
> really. Pick something that is machine friendly...4,8, 16K
>
> I usually start them off with a return code and a message, then the
> parameters I'm interested in. Some of mine include an "action code" as
> well,
> so they can do different things (e.g. the Interface Block to an access
> module might specify an action code of "INSERT", "SELECT","UPDATE", or
> "DELETE", along with a common buffer area, and any other parameters.
>
> "Generalizing" parameterization is a very desirable thing, because the
> fewer
> changes you make to interfaces, the fewer problems you are likely to
> encounter later. I like parameters that are just...
>
> LINKAGE SECTION.
> 01 passed-data pic x(8192).
>
> then, before calling
>
> ...move progname-interface-block to passed data
> call progname
> using passed-data
> ....
>
> Upon receiving...
>
> move passed-data to ws-interface-block
>
> Before returning...
>
> move ws-interface-block to passed-data
> ...
>
> Where progname-interface-block and ws-interface-block are the same

COPYbook.
>
> Now, yes, this does mean you have two copies of the interface block (one
> in
> the calling program, and one in the called one) rather than just a
> single
> shared copy as you would through linkage. For some people, this would
> not be
> acceptable, but I find there are actually some benefits to it.
> (Interactive
> debugging is greatly simplified for a start, but many people here
> apparently
> dont use interactive debugging anyway, so for them it is academic. )
>
> Another reason why this doesn't bother me is because the interface block
> is
> usually much smaller than the full 8K allocated, so it isn't as wasteful
> as
> you might think.
>
> The major benefit is that you can change the interface block and extend
> it,
> WITHOUT modifying the program linkage or interfacing.
>
> This is particularly useful when dealing with nested COBOL programs.
> (Fujitsu OO COBOL implements Class Methods through nested
> sub-programming)
>
> (You need to recompile all programs using the block so they all have the
>
> latest COPY, but you'd need to do that anyway if you did it through
> Linkage.)


Actually, this is *very* similar to my idea. I also have a linkage section
data area (the one with the ODO) that I copy to a working-storage area. I
then copy it back at the end, just like you are doing.

The main difference between mine and yours is that if I change the copybook
that describes the interface block there is no need to recompile *all*
programs that utilize the interface block. I need only recompile those
programs that care about the new data that I've added to the end. Indeed,
this is the whole point I am trying to make. Currently we have a particular
subroutine that is used by probably over 100 programs. If there was ever a
need to add a new field to the interface block it would require recompiling
every single one of those 100+ programs. To me that's just not ! So my
thought is to add a length field to the beginning of the interface block and
have each calling program set the value to the length of the rest of the
interface block. This way the called program is free to write to *it's
working-storage* version of the interface block, which may be longer than
the working-storage version of the interface block in the calling program.
But when the called program copies the data back to the linkage section
(which is actually the working-storage area of the caller) it knows the size
that the caller has defined it as, and thus can make sure that it does not
wipe out any extra data in the caller's working-storage.

So in my opinion my method has all of the benefits of your method, with the
minor additional pain that the caller needs to make sure to set the length
field prior to making the call. But mine has the additional benefit that
there is no need to recompile programs that do not care about new fields
that may be added to the interface block copybook.

If my compiler supported the ANY LENGTH clause on linkage section data items
it would be absolutely perfect! (Well, close enough.) I would have the
following:

'COPY.CPY':
01 LINK-AREA.
05 LINK-FIELD-1 PIC X(8).
05 LINK-FIELD-2 PIC X(8).
05 LINK-FIELD-3 PIC X(10).
05 LINK-FIELD-4 PIC X(1).

'CALLEE.COB':
IDENTIFICATION DIVISION.
PROGRAM-ID. CALLEE.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY 'COPY.CPY'.
LINKAGE SECTION.
01 LS-LINK-AREA PIC X ANY LENGTH.
PROCEDURE DIVISION USING LS-LINK-AREA.
MOVE LS-LINK-AREA TO LINK-AREA
MOVE ALL '-' TO LINK-FIELD-4
DISPLAY 'CALLEE'
DISPLAY LINK-FIELD-4
MOVE LINK-AREA TO LS-LINK-AREA
EXIT PROGRAM.

'CALLER.COB':
PROGRAM-ID. CALLEE AS 'CALLEE' IS PROTOTYPE.
LINKAGE SECTION.
01 LS-LINK-AREA PIC X ANY LENGTH.
PROCEDURE DIVISION USING LS-LINK-AREA.
END PROGRAM. CALLEE

IDENTIFICATION DIVISION.
PROGRAM-ID. CALLER.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY 'COPY.CPY'.
01 MISC PIC X(120) VALUE ALL 'Q'.
PROCEDURE DIVISION.
MOVE ALL '1' TO LINK-FIELD-1
MOVE ALL '2' TO LINK-FIELD-2
MOVE ALL '3' TO LINK-FIELD-3
CALL CALLEE USING LINK-AREA
DISPLAY 'CALLER'
DISPLAY LINK-FIELD-1
DISPLAY LINK-FIELD-2
DISPLAY LINK-FIELD-3
DISPLAY LINK-FIELD-4
DISPLAY '(' MISC ')'
STOP RUN.

The differences between this version and my original version are that I no
longer have the length field in the copybook or in the linkage section of
the called program. Instead the linkage section of the called program has
it's data area defined as "PIC X ANY LENGTH". This way the length of the
data area passed from the caller to the callee is passed implicitly. The
'program prototype' of CALLEE defined in CALLER is needed so that CALLER
knows that CALLEE expects an implicit length field to be passed to it
(because of the ANY LENGTH clause). (I am unsure if my definition of the
program prototype is correct, but I think it is close. I could never get it
working as I expected under MF NE 5.0 UE, but it's been a while since I
tried it.)

With the above I am free to add fields to the COPY copybook willy-nilly
without any need to change *or re-compile* CALLER if CALLER does not care
about these new fields.

> Here's an example:


[deleted]

>
> Note there is no filler at the end.
>
> To change the length of any of the fields, or to add new ones, is dead
> easy
> in COBOL (it can become more problematic in other languages, see below)
>
> Change the block, amend the affected programs that use the new fields,
> recompile all programs using the new block... done. Exactly the same
> process
> as you would use if you had the block defined in Linkage.
>
> No messing with fillers, no counting bytes and setting the count into
> length
> parameters (then finding it was wrong :-)).
>
> It is straightforward and simple. Why mess with OCCURS ...DEPENDING...?


For the reason I specified, of course! :-) No need for recompiling. Let's
say you are a vendor with several products, and each product calls a shared
subroutine (DLL). Wouldn't it be nice to be able to change the subroutine
DLL and the one related main program (EXE) that cares about the change to
the subroutine, but you wouldn't have to deliver a new version of every
other program that uses that subroutine?

Personally, I don't find ODO that confusing. And as for counting bytes, I
am not doing that. I am just using the COBOL "LENGTH OF" operator (or
FUNCTION LENGTH intrinsic function, if you prefer) to do it for me.
Hopefully COBOL knows how to count! :-)

I'm not going to address the C# -> COBOL interface in detail. It seems to
me, though, that the whole philosophy of how data is passed back and forth
to OO programs is different than for straight procedural programs (or COBOL,
at least). With OO design it's really the called program, rather than the
calling program, that allocates the storage for the interface block (or
blocks!). This way the called program can allocate storage for new fields
whenever it wants, and if the called program doesn't care about them then it
doesn't care. Of course with OO you usually don't have monolithic blocks,
as in regular COBOL, but lots of little ones. I think they are called
'objects'. ;-)

I don't think this really solves your issue, because you already have the
COBOL subroutine built using the 'monolithic' interface block. Of course
you could probably rewrite it, but then as you say you probably wouldn't use
COBOL.

Frank

Frank Swarbrick

2007-07-25, 6:55 pm

>>> On 7/24/2007 at 10:18 PM, in message
< Gr6dnQRo2Y4ZUjvbnZ2dnUVZ_u6rnZ2d@comcast
.com>, LX-i<lxi0007@netscape.net>
wrote:
> Pete Dashwood wrote:
> the
> unwieldy.
> not
>
> What you might look into is creating a description of your "interface
> block" in XML, then parsing that document and spinning through the nodes
>
> to build your interface block. You'd still have to modify (or extend)
> the class if you added a new property, but you could have a generic
> "Generate Block" getter that assembled it according to the specs in the
> file.
>
> Something like...
>
> <interface_block size="8192">
> <parameter name="street" position="1" size="150" />
> <parameter name="city" position="2" size="50" />
> ...
> </interface_block>
>
> (The position attribute is just so you can retrieve them in the right
> order, even if they're not in the right order in the XML file.) Judging
>
> from the way you've talked about your components, having a reusable
> configuration for interface blocks might be nice.


Oy! Now you are adding the overhead of XML serialization / de-serialization
to each call. Not that I haven't considered it, but it just seems horribly
inefficient to me. Leave XML for passing data between heterogenous systems!
:-)

Frank


Frank Swarbrick

2007-07-25, 6:55 pm

>>> On 7/25/2007 at 4:51 AM, in message
<1185360708.315094.272660@57g2000hsv.googlegroups.com>,
Alistair<alistair@ld50macca.demon.co.uk> wrote:

> Pete Dashwood wrote:
> gloves,
>
> I love OCCURS DEPENDING ON. Fits in nicely with PERFORM VARYING, etc.
> I can only guess that you had an unfortunate experience in your
> formative years (in IT) with ODO.


It also works wonderfully 'dynamic length' tables and the SEARCH statement!
(If only they were actual real dynamic tables... I can't believe that the
next standard wants to make real dynamic length tables optional instead of
mandatory!!!)

I just implemented a project last month that made extensive use of ODO
tables. Each program builds the table from data in a file. Specifically,
it's a file of 'card types'. In the past when we've added a new card type
we've had to go in to each program that has a card type table or similar
item in it and add a new occurrence for the new card type. Now I simply
dynamically build the table, increasing the value of the ODO for each type.

Let me give some real world code, as I am terrible coming up with fake
examples. They always come out...fake!

working-storage section.
REPLACE ==:CTMAX:== BY ==20==.
01 CARD-TYPE-TABLE.
05 CTC PIC S9(3) COMP-3 VALUE +0.
05 CTT-ENTRY OCCURS 0 TO :CTMAX:
DEPENDING ON CTC INDEXED BY CTI.
10 CTT-TYP PIC X.
10 CTT-DESCR PIC X(4).
10 CTT-CNT PIC S9(7) COMP-3 VALUE ZEROES.
10 CTT-RECENT-CNT PIC S9(7) COMP-3 VALUE ZEROES.

procedure division.

* Here we dynamically build the card type table
0100-BUILD-CARD-TYPE-TABLE.
SET CTI TO 1.
OPEN INPUT CTR-FILE.
PERFORM UNTIL CTF-AT-END
READ CTR-FILE
AT END CONTINUE
NOT AT END
PERFORM 0100-CARD-TYPE-RECORD
END-READ
END-PERFORM.
CLOSE CTR-FILE.

0100-CARD-TYPE-RECORD.
ADD 1 TO CTC.
IF CTC > :CTMAX:
DISPLAY 'CARD-TYPE-TABLE: TABLE LENGTH EXCEEDED'
UPON CONSOLE
GOBACK
END-IF.
MOVE CTR-TYPE-CODE TO CTT-TYP(CTI).
MOVE CTR-SHORT-DESCR TO CTT-DESCR(CTI).
SET CTI UP BY 1.

* Here's an example of finding the table occurrence and utilizing
* it
MOVE S-PAN TO CHR-PAN-ACCOUNT.
READ CHF-FILE
INVALID KEY
SET BAD-CHF-READ TO TRUE.
IF GOOD-CHF-READ
IF CHR-CARD-TYPE NOT = CTT-TYP(CTI)
SET CTI TO 1
SEARCH CTT-ENTRY
AT END
STOP 'UNKNOWN CARD TYPE - LEAVE MSG FOR PROGRAMMING'
DISPLAY CHR-PAN-ACCOUNT ' ' CHR-CARD-TYPE
GOBACK
WHEN CHR-CARD-TYPE = CTT-TYP(CTI)
CONTINUE
END-SEARCH
END-IF
ADD +1 TO CTT-CNT(CTI)
END-IF.

* Later...CTI is still set appropriately:
IF ATM-TRANSES-THIS-CARD > ZERO
OR POS-TRANSES-THIS-CARD > ZERO
ADD +1 TO CARDS-USED-RECENTLY
ADD +1 TO CTT-RECENT-CNT(CTI)
END-IF.

* Here are some reporting routines
PERFORM VARYING CTI FROM 1 BY 1 UNTIL CTI > CTC
MOVE CTT-CNT(CTI) TO F2-CARD-CNT
MOVE CTT-DESCR(CTI) TO F2-CARD-TYPE
MOVE F2-COUNTS-LINE TO CMP-PRINT2
MOVE SPACE TO CMP-RECORD2-CC
PERFORM X-010-WRITE-CMP2 THRU X-010-EXIT
END-PERFORM.

PERFORM VARYING CTI FROM 1 BY 1 UNTIL CTI > CTC
MOVE CTT-RECENT-CNT(CTI) TO F2-CARD-CNT
MOVE CTT-DESCR(CTI) TO F2-CARD-TYPE
MOVE F2-COUNTS-LINE TO CMP-PRINT2
MOVE SPACE TO CMP-RECORD2-CC
PERFORM X-010-WRITE-CMP2 THRU X-010-EXIT
END-PERFORM.

In the end, as long as we don't have more than 20 card types (see :CTMAX:
above) we should never have to touch this program just because a new card
type has been added to the CTR-FILE. Even if we get more than 20 (we have
only seven right now) we can just bump up that value and recompile (every
single program that has it, of course).

Not perfect, but pretty good for COBOL! :-)

Frank

Frank Swarbrick

2007-07-25, 6:55 pm

>>> On 7/25/2007 at 6:10 AM, in message
<5gostpF3h90juU1@mid.individual.net>,
Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:

>
> "Alistair" <alistair@ld50macca.demon.co.uk> wrote in message
> news:1185360708.315094.272660@57g2000hsv.googlegroups.com...
> No Doctor Freud, not at all...:-)
>
> I realise there will be much disagreement on this and it is really
> academic
> as far as I'm concerned. I'm moving away from COBOL so I can't get
> passionate about it.
>
> Certainly in the early days, the overheads were much more important.
>
> Nevertheless, in a career spanning 40 years, I have never coded this
> construct from choice. I have dealt with it when doing maintenance and
> when
> it is a pre-existing part of live code, but you won't find anything
> written
> by me, that I had any control over, which contains this ghastly
> construct.
>
> I can summarize my objections as follows:
>
> 1. It is just...ugly.
> 2. It requires a control field to be set before you can do anything with
> it.
> 3. It doesn't deliver what it promises (that is always a show-stopper for


> me
> :-)). It makes you think it is saving you memory, but it still allocates
> the
> maximum, so what exactly is the point?
> 4. It behaves differently across different platforms, depending on what
> compile options you set. (So much for transportability...)
> 5. It introduces an unnecessary overhead. (that doesn't bother me so
> much; I
> realise modern optimizing compilers can minimise this but, at the end of
> the
> day, it is going to generate indirect addressing through registers on
> most
> machines, when many of them have perfectly good hardware for moving
> stuff
> directly.)
> 6. It requires more writing in source. Anything (apart from pertinent
> documentation and formatting) that makes the source code bigger gets
> black
> marks from me.
> 7. It is a frequent source of errors and crashes.(The control field gets
> set
> incorrectly for any number of reasons, or it doesn't get set at all for
> any
> number of reasons. These can be data related, and very hard to pick up
> during development testing. Live data comes along and once every couple
> of
> years it crashes.
> 8. Did I mention it was ugly?
>
> OK, that's enough time wasted on something that is a waste of time in
> itself...
>
> If you or others LOVE it, great. I wish you happiness :-)
>
> And there is NO connection between PERFORM...VARYING (which is an
> incredibly
> useful construct, invaluable, in fact) and OCCURS...DEPENDING ON (which
> isn't...)


Some valid objections. Some maybe less so. One thing I didn't mention in
my previous posting was the usefulness of ODO and the SUM intrinsic
function.
Check this out:

01 MY-TABLE.
05 MY-COUNT PIC 9(3).
05 MY-ITEM OCCURS 0 TO 20 TIMES
DEPENDING ON MY-COUNT
INDEXED BY MY-IDX.
10 MY-DESCR PIC X(10).
10 MY-TOTAL PIC S9(9)V99.

PERFORM VARYING MY-IDX FROM 1 BY 1 UNTIL MY-IDX > MY-COUNT
MOVE MY-DESCR(MY-IDX) TO DTL-DESCR
MOVE MY-TOTAL(MY-IDX) TO DTL-TOTAL
PERFORM WRITE-DETAIL-LINE
END-PERFORM
MOVE 'TOTALS' TO DTL-DESCR
COMPUTE DTL-TOTAL = FUNCTION SUM (MY-TOTAL(ALL))
PERFORM WRITE-DETAIL-LINE

The interesting line is the second to last one. While the table is taking
up storage for 20 items, since MY-COUNT is only set to, say 7, then the SUM
(MY-TOTAL(ALL)) sums up only those 7 occurrences. Of course I suppose the
other MY-TOTAL values would all be zero, so I guess it's a difference that
makes no real difference... But I think it's kind of anyway. What
would make a difference is if you were using the MEDIAN or the MEAN
functions, in which case the number of occurrences also makes a difference.

Anyway, just something I came upon when working on this project.

Frank

Robert Jones

2007-07-25, 6:55 pm

On Jul 25, 6:18 pm, "Frank Swarbrick" <Frank.Swarbr...@efirstbank.com>
wrote:
>
> <1185360708.315094.272...@57g2000hsv.googlegroups.com>,
>
> Alistair<alist...@ld50macca.demon.co.uk> wrote:
>
>
>
>
> It also works wonderfully 'dynamic length' tables and the SEARCH statement!
> (If only they were actual real dynamic tables... I can't believe that the
> next standard wants to make real dynamic length tables optional instead of
> mandatory!!!)
>
> I just implemented a project last month that made extensive use of ODO
> tables. Each program builds the table from data in a file. Specifically,
> it's a file of 'card types'. In the past when we've added a new card type
> we've had to go in to each program that has a card type table or similar
> item in it and add a new occurrence for the new card type. Now I simply
> dynamically build the table, increasing the value of the ODO for each type.
>
> Let me give some real world code, as I am terrible coming up with fake
> examples. They always come out...fake!
>
> working-storage section.
> REPLACE ==:CTMAX:== BY ==20==.
> 01 CARD-TYPE-TABLE.
> 05 CTC PIC S9(3) COMP-3 VALUE +0.
> 05 CTT-ENTRY OCCURS 0 TO :CTMAX:
> DEPENDING ON CTC INDEXED BY CTI.
> 10 CTT-TYP PIC X.
> 10 CTT-DESCR PIC X(4).
> 10 CTT-CNT PIC S9(7) COMP-3 VALUE ZEROES.
> 10 CTT-RECENT-CNT PIC S9(7) COMP-3 VALUE ZEROES.
>
> procedure division.
>
> * Here we dynamically build the card type table
> 0100-BUILD-CARD-TYPE-TABLE.
> SET CTI TO 1.
> OPEN INPUT CTR-FILE.
> PERFORM UNTIL CTF-AT-END
> READ CTR-FILE
> AT END CONTINUE
> NOT AT END
> PERFORM 0100-CARD-TYPE-RECORD
> END-READ
> END-PERFORM.
> CLOSE CTR-FILE.
>
> 0100-CARD-TYPE-RECORD.
> ADD 1 TO CTC.
> IF CTC > :CTMAX:
> DISPLAY 'CARD-TYPE-TABLE: TABLE LENGTH EXCEEDED'
> UPON CONSOLE
> GOBACK
> END-IF.
> MOVE CTR-TYPE-CODE TO CTT-TYP(CTI).
> MOVE CTR-SHORT-DESCR TO CTT-DESCR(CTI).
> SET CTI UP BY 1.
>
> * Here's an example of finding the table occurrence and utilizing
> * it
> MOVE S-PAN TO CHR-PAN-ACCOUNT.
> READ CHF-FILE
> INVALID KEY
> SET BAD-CHF-READ TO TRUE.
> IF GOOD-CHF-READ
> IF CHR-CARD-TYPE NOT = CTT-TYP(CTI)
> SET CTI TO 1
> SEARCH CTT-ENTRY
> AT END
> STOP 'UNKNOWN CARD TYPE - LEAVE MSG FOR PROGRAMMING'
> DISPLAY CHR-PAN-ACCOUNT ' ' CHR-CARD-TYPE
> GOBACK
> WHEN CHR-CARD-TYPE = CTT-TYP(CTI)
> CONTINUE
> END-SEARCH
> END-IF
> ADD +1 TO CTT-CNT(CTI)
> END-IF.
>
> * Later...CTI is still set appropriately:
> IF ATM-TRANSES-THIS-CARD > ZERO
> OR POS-TRANSES-THIS-CARD > ZERO
> ADD +1 TO CARDS-USED-RECENTLY
> ADD +1 TO CTT-RECENT-CNT(CTI)
> END-IF.
>
> * Here are some reporting routines
> PERFORM VARYING CTI FROM 1 BY 1 UNTIL CTI > CTC
> MOVE CTT-CNT(CTI) TO F2-CARD-CNT
> MOVE CTT-DESCR(CTI) TO F2-CARD-TYPE
> MOVE F2-COUNTS-LINE TO CMP-PRINT2
> MOVE SPACE TO CMP-RECORD2-CC
> PERFORM X-010-WRITE-CMP2 THRU X-010-EXIT
> END-PERFORM.
>
> PERFORM VARYING CTI FROM 1 BY 1 UNTIL CTI > CTC
> MOVE CTT-RECENT-CNT(CTI) TO F2-CARD-CNT
> MOVE CTT-DESCR(CTI) TO F2-CARD-TYPE
> MOVE F2-COUNTS-LINE TO CMP-PRINT2
> MOVE SPACE TO CMP-RECORD2-CC
> PERFORM X-010-WRITE-CMP2 THRU X-010-EXIT
> END-PERFORM.
>
> In the end, as long as we don't have more than 20 card types (see :CTMAX:
> above) we should never have to touch this program just because a new card
> type has been added to the CTR-FILE. Even if we get more than 20 (we have
> only seven right now) we can just bump up that value and recompile (every
> single program that has it, of course).
>
> Not perfect, but pretty good for COBOL! :-)
>
> Frank


I also quite like occurs depending on for similar reasons, it makes
searches and table sorts much faster when the table is only partially
full and the first/controlling index is set appropriately. Of course
use of RDBMS makes much of its use unnecessary in such environments.

Robert

Robert Jones

2007-07-25, 6:55 pm

On Jul 25, 8:04 pm, Robert Jones <rjon...@hotmail.com> wrote:
> On Jul 25, 6:18 pm, "Frank Swarbrick" <Frank.Swarbr...@efirstbank.com>
> wrote:
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> I also quite like occurs depending on for similar reasons, it makes
> searches and table sorts much faster when the table is only partially
> full and the first/controlling index is set appropriately. Of course
> use of RDBMS makes much of its use unnecessary in such environments.
>
> Robert


Oops, I should have said that it is the occurs depending on count
that is to be set rather than an index, which isn't even necessary for
a variable length table.

LX-i

2007-07-25, 6:55 pm

Frank Swarbrick wrote:
> < Gr6dnQRo2Y4ZUjvbnZ2dnUVZ_u6rnZ2d@comcast
.com>, LX-i<lxi0007@netscape.net>
> wrote:
>
> Oy! Now you are adding the overhead of XML serialization / de-serialization
> to each call. Not that I haven't considered it, but it just seems horribly
> inefficient to me. Leave XML for passing data between heterogenous systems!
> :-)


Nah - parse it once at application startup and cache it. Then it's
yours to use at will! :)


--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ / \/ _ o ~ Live from Albuquerque, NM! ~
~ _ /\ | ~ ~
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
~ Business E-mail ~ daniel @ "Business Website" below ~
~ Business Website ~ http://www.djs-consulting.com ~
~ Tech Blog ~ http://www.djs-consulting.com/linux/blog ~
~ Personal E-mail ~ "Personal Blog" as e-mail address ~
~ Personal Blog ~ http://daniel.summershome.org ~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~

GEEKCODE 3.12 GCS/IT d s-:+ a C++ L++ E--- W++ N++ o? K- w$ !O M--
V PS+ PE++ Y? !PGP t+ 5? X+ R* tv b+ DI++ D+ G- e h---- r+++ z++++

"Who is more irrational? A man who believes in a God he doesn't see,
or a man who's offended by a God he doesn't believe in?" - Brad Stine
Pete Dashwood

2007-07-25, 6:55 pm

Thanks Frank. Good response, comments below...

"Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
news:46A721E1.6F0F.0085.0@efirstbank.com...
> <5gnr6mF3hba38U1@mid.individual.net>,
> Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:
>
<snip>
[color=darkred]
>
> Actually, this is *very* similar to my idea. I also have a linkage
> section
> data area (the one with the ODO) that I copy to a working-storage area. I
> then copy it back at the end, just like you are doing.
>
> The main difference between mine and yours is that if I change the
> copybook
> that describes the interface block there is no need to recompile *all*
> programs that utilize the interface block.


That is true for my approach as well; I just think it is safer and more
consistent to ensure that ALL programs are on the same hymn sheet when it
comes to COPY books.


> I need only recompile those
> programs that care about the new data that I've added to the end.


It isn't just the "additions"; you may also have extened fields within the
block, or changed their usage. (I did this with the example interface block
I posted, yesterday. Fortunately, it was a very easy amendment to both the
COBOL and the C# code and, to my surprise, everything worked perfectly after
the changes...:-))

>Indeed,
> this is the whole point I am trying to make. Currently we have a
> particular
> subroutine that is used by probably over 100 programs. If there was ever
> a
> need to add a new field to the interface block it would require
> recompiling
> every single one of those 100+ programs.


That is the point, indeed. By having an "anonymous" interface (pic x (8192),
for example) you DON'T have to do this.

>To me that's just not !


This is where a .DLL comes into its own. Having an interface object that is
dissociated from the programs, with encapsulated functionality like
validation removed from the programs, makes life a lot easier.

>So my
> thought is to add a length field to the beginning of the interface block
> and
> have each calling program set the value to the length of the rest of the
> interface block. This way the called program is free to write to *it's
> working-storage* version of the interface block, which may be longer than
> the working-storage version of the interface block in the calling program.
> But when the called program copies the data back to the linkage section
> (which is actually the working-storage area of the caller) it knows the
> size
> that the caller has defined it as, and thus can make sure that it does not
> wipe out any extra data in the caller's working-storage.
>


Hmmm... it sounds a bit "hairy", Frank :-)

Nevertheless, I'm sre you can get this to work and it certainly has benefits
over the standard approach.

> So in my opinion my method has all of the benefits of your method, with
> the
> minor additional pain that the caller needs to make sure to set the length
> field prior to making the call.


And the risk of only compiling programs that are affected... I don't like
that bit. You could have programs using the same COPY book but with
different versions of it...

> But mine has the additional benefit that
> there is no need to recompile programs that do not care about new fields
> that may be added to the interface block copybook.


No, my approach COULD do that, too. I just don't like it, for the reasons
outlined. I believe if I had 100+ programs that all need a particular piece
of functionality, I would definitely encapsulate this into a Class. In
standard COBOL, it has to be a called subroutine, and that brings us to the
interface... I would use an interface block, as described.


>
> If my compiler supported the ANY LENGTH clause on linkage section data
> items
> it would be absolutely perfect! (Well, close enough.) I would have the
> following:


If wishes were horses, beggars would ride...:-)

> With the above I am free to add fields to the COPY copybook willy-nilly
> without any need to change *or re-compile* CALLER if CALLER does not care
> about these new fields.
>

Ah, if only... :-)


>
> [deleted]
>
>
> For the reason I specified, of course! :-) No need for recompiling.
> Let's
> say you are a vendor with several products, and each product calls a
> shared
> subroutine (DLL). Wouldn't it be nice to be able to change the subroutine
> DLL and the one related main program (EXE) that cares about the change to
> the subroutine, but you wouldn't have to deliver a new version of every
> other program that uses that subroutine?


That's exactly what I get with OO... :-)
>
> Personally, I don't find ODO that confusing.


That was never the issue. I don't find it confusing either; I do find it
inelegant.

>And as for counting bytes, I
> am not doing that. I am just using the COBOL "LENGTH OF" operator (or
> FUNCTION LENGTH intrinsic function, if you prefer) to do it for me.
> Hopefully COBOL knows how to count! :-)
>


Yes, these new-fangled intrinsic functions have really revolutionized
COBOL... :-)



> I'm not going to address the C# -> COBOL interface in detail. It seems to
> me, though, that the whole philosophy of how data is passed back and forth
> to OO programs is different than for straight procedural programs (or
> COBOL,
> at least). With OO design it's really the called program, rather than the
> calling program, that allocates the storage for the interface block (or
> blocks!). This way the called program can allocate storage for new fields
> whenever it wants, and if the called program doesn't care about them then
> it
> doesn't care. Of course with OO you usually don't have monolithic blocks,
> as in regular COBOL, but lots of little ones. I think they are called
> 'objects'. ;-)
>
> I don't think this really solves your issue, because you already have the
> COBOL subroutine built using the 'monolithic' interface block. Of course
> you could probably rewrite it, but then as you say you probably wouldn't
> use
> COBOL.
>


Fortunately, the COBOL is now wrapped in C# and working correctly through
Interop Services of the DotNET Framework. The interface in C# (which I
posted) also works fine, so everything in that particular garden is rosy...
:-)

Good luck with your variable interfacing, Frank. I think your approach is
imaginative and worth doing, but rather you than me :-)

Pete.
--
"I used to write COBOL...now I can do anything."


Pete Dashwood

2007-07-25, 9:55 pm



"LX-i" <lxi0007@netscape.net> wrote in message
news:Gr6dnQRo2Y4ZUjvbnZ2dnUVZ_u6rnZ2d@co
mcast.com...
> Pete Dashwood wrote:
>
> What you might look into is creating a description of your "interface
> block" in XML, then parsing that document and spinning through the nodes
> to build your interface block. You'd still have to modify (or extend) the
> class if you added a new property, but you could have a generic "Generate
> Block" getter that assembled it according to the specs in the file.
>
> Something like...
>
> <interface_block size="8192">
> <parameter name="street" position="1" size="150" />
> <parameter name="city" position="2" size="50" />
> ...
> </interface_block>
>
> (The position attribute is just so you can retrieve them in the right
> order, even if they're not in the right order in the XML file.) Judging
> from the way you've talked about your components, having a reusable
> configuration for interface blocks might be nice.
>

I have a "reusable configuration" for interface blocks; I just instantiate
an instance whenever I need one :-)

(I take your point that if the interfaces change, it might be useful to have
a flexible means of implementing them. I am addressing this through a
generator. The generator certainly COULD produce XML, but I have decided
against doing this for the reasons outlined below. Instead, it will produce
a C# Class, that is immediately built into a .DLL and loaded into the Global
Assembly Cache.)

I gave this some thought and here's what I think:

1. There is a (big?) difference between global parameters that remember
system state, and local parameters that are part of program interfacing.
2. While it is possible to implement both using XML, I don't believe it is
desirable.

The use of interface objects as provided through a .DLL is much better for
interprogram communication. It is secure, and managed entirely by the
entities concerned with its content. No possibility of any external
meddling...no XML processing overheads... enjoys the benefits of private
and/or protected status... and can only be accessed through a single
interface (GET,SET).

So for "run time" parameters passed between programs, this gets my vote.

If there are changes to an Interface Class (like the one I posted), I would
make those changes and rebuild the .DLL. This has minimum impact on the
existing system (no recompilation of any Assemblies), is efficient, and
secure. While XML could do the same job, it could be subject to external
manipulation. These days, the overhead in using XML is negligible (and you
could load the structure during initialization), but you still have
something that is readable (and accessible) by Humans, when it is intended
for program consumption only. Manipulating XML dynamically, can be a bit
unwieldy as well.

However, XML is an incredibly powerful tool. The DotNET FCL has
comprehensive facilities for managing it easily and I have already found it
to be useful for remembering things like user options and system state. In
some instances, where the overhead of a RDB is hardly warranted, XML serves
better.

I think the way I'm going is:

1. Anything that could be of interest to a human ("last used", global
options and settings, read-only datasets where a RDB is simply overkill...)
use XML

2. Interface blocks for inter-program communication...Create an Interface
Class and instantiate an object of it.

Thanks for your post and ideas, Daniel.

Pete.
--
"I used to write COBOL...now I can do anything."


Frank Swarbrick

2007-07-25, 9:55 pm

>>> On 7/25/2007 at 5:08 PM, in message
<lp6dnX6-be_sRTrbnZ2dnUVZ_ufinZ2d@comcast.com>, LX-i<lxi0007@netscape.net>
wrote:
> Frank Swarbrick wrote:
LX-i<lxi0007@netscape.net>[color=darkred]
[color=darkred]
>
> de-serialization
> horribly
> systems!
>
> Nah - parse it once at application startup and cache it. Then it's
> yours to use at will! :)


I'm not sure what you mean. Oh, wait, I see what you're doing now. I
misunderstood. I thought you were sending the data itself as XML. You're
sending the meta-data, as it were, that describes the interface block. But
the interface block itself is still just a big block of data. Interesting!

Frank

Frank Swarbrick

2007-07-25, 9:55 pm

>>> On 7/25/2007 at 5:17 PM, in message
<5gq40mF3heurnU1@mid.individual.net>,
Pete Dashwood<dashwood@removethis.enternet.co.nz> wrote:
> Thanks Frank. Good response, comments below...
>
> "Frank Swarbrick" <Frank.Swarbrick@efirstbank.com> wrote in message
> news:46A721E1.6F0F.0085.0@efirstbank.com...
> <snip>
>
the[color=darkred]
[color=darkred]
> I
>
> That is true for my approach as well; I just think it is safer and more
> consistent to ensure that ALL programs are on the same hymn sheet when
> it
> comes to COPY books.


I guess I'm still not quite getting what you're saying. Are you saying that
the CALLER always defines 8192 bytes of working-storage as well? This is
fine, as long as you're never going to need more that 8192 bytes. Also,
then you say there's no filler on the end. But there must be filler between
the end of the relevant data and the end of the 8192 bytes.

>
>
> It isn't just the "additions"; you may also have extened fields within
> the
> block, or changed their usage. (I did this with the example interface
> block
> I posted, yesterday. Fortunately, it was a very easy amendment to both
> the
> COBOL and the C# code and, to my surprise, everything worked perfectly
> after
> the changes...:-))


No argument there. Certainly if you're changing the length of existing
fields you are stuck with recompiling every program.

> ever
>
> That is the point, indeed. By having an "anonymous" interface (pic x
> (8192),
> for example) you DON'T have to do this.
>
>
> This is where a .DLL comes into its own. Having an interface object that
> is
> dissociated from the programs, with encapsulated functionality like
> validation removed from the programs, makes life a lot easier.
>
[color=darkred]
>
than[color=darkred]
> program.
> not
>
> Hmmm... it sounds a bit "hairy", Frank :-)
>
> Nevertheless, I'm sre you can get this to work and it certainly has
> benefits
> over the standard approach.
>
> length
>
> And the risk of only compiling programs that are affected... I don't
> like
> that bit. You could have programs using the same COPY book but with
> different versions of it...


Different only in that more recently compiled programs could have additional
fields at the end. And, of cour