If the Compiler Can’t Find the Mistake, Maybe You Can
August 22, 2007 Ted Holt
Programs should not break when people change them, yet they often do. Sometimes programs break when programmers modify only one of two things that have to stay in sync. Compilers can’t always catch such errors, but you often can.
For example, suppose that you are developing a program with a control break. Regardless of what language you are using, you will need a holding variable for each control field in order to test when a control field changes. Here’s an example of such a variable.
D SaveItemNo s like(ItemNo)
ItemNo is a database field. SaveItemNo is defined to be like ItemNo. If the size of ItemNo ever increases, the size of SaveItemNo will also increase, and the control break logic will continue to work correctly.
However, suppose the original programmer did not use the like keyword, but defined SaveItemNo to have the appropriate length and decimal positions.
D SaveItemNo s 7a
A maintenance programmer would have to modify the definition of SaveItemNo whenever the definition of ItemNo changed. If the programmer defined ItemNo without redefining SaveItemNo, the program would probably not work correctly.
Here’s another example, in which the GrandTotal variable is defined for the summing up a number.
D GrandTotal s +2 like(Total)
GrandTotal is defined to be two digits larger than Total, with the same number of decimal positions. No matter how often Total is redefined, GrandTotal will always be two digits larger than Total.
The like keyword is a great way to ensure that data definitions stay in sync. By the way, DDS and COBOL also have corresponding methods of defining data to be like other data.
But the compiler cannot check all such dependencies. Look at this example:
D Company s 2a D Customer s 5a // Key must be as long as Company and Customer together D Key s 7a C movel Company Key C move Customer Key C Key chain SomeFile
I have seen many programs with code like this. The developer has thoughtfully included a comment that explains how Key is defined in relation to the Company and Customer fields. But what if a maintenance programmer doesn’t see this comment?
In this case, there is no way to tell the compiler that Key must be as large as Company and Customer together. But you can enforce the dependency yourself. RPG IV includes built-in functions that access data definition.
Here’s the same code, with an assertion that verifies that Key is defined correctly.
H dftactgrp(*no) actgrp(*new) H bnddir('TOOLKITBD') D/copy prototypes,assert D Company s 3a D Customer s 5a // Key must be as long as Company and Customer together D Key s 7a /free *inlr = *on; assert (%size(Company) + %size(Customer) = %size(Key): 'Key must be as long as Company and Customer combined'); /end-free C movel Company Key C move Customer Key C Key chain SomeFile /free return;
If you’re not familiar with assertions, you may want to read Cletus the Codeslinger’s article about that very topic. If someone redefines Company or Customer, but does not refine Key accordingly, the program will rudely cancel upon first invocation (which will be in a test environment!) The program will not run until the dependency is fixed. You don’t have to use an assertion, of course. Any mechanism that alerts someone to the problem is OK.
Here’s an example from a program I wrote recently. There are two arrays–Data and Attr. In this program, it is imperative that the two arrays have identically-defined elements, but Data must have two elements more than Attr.
H dftactgrp(*no) actgrp(*new) H bnddir('TOOLKITBD') D/copy prototypes,assert D*DataDim c const(6) D DataDim c const(8) D Data s 40a dim(DataDim) D D AttrDim c const(4) D Attr s like(Data) dim(AttrDim) /free *inlr = *on; assert (%elem(Data) - %elem(Attr) = 2: 'Array Attr must have exactly 2 fewer elements than + array Data');
Again, an assertion placed at the beginning of the calculation specs prevents the program from running if either array’s dimension is changed without the other array’s being adjusted accordingly.
Here’s one last example. Packed variables SomeQty and SomePct are supposed to have the same number of digits, but SomePct, which is a percentage, must have two more decimal places. The assertion verifies that this is so.
H dftactgrp(*no) actgrp(*new) H bnddir('TOOLKITBD') D/copy prototypes,assert // SomeQty and SomePct must have the same number of digits, // but SomePct must have 2 more decimal positions. D SomeQty s 7p 2 D SomePct s 7p 4 /free *inlr = *on; assert (%size(SomeQty)=%size(SomePct) and %decpos(SomePct)-%decpos(SomeQty)=2: 'Invalid definition of SomeQty and/or SomePct');
It’s a standing joke in many shops that any program that compiles must be correct. We all know that that’s not necessarily the case. Since the compiler can only go so far, it’s up to developers to add strength to the programs our employers depend on.