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





What are my options for modifying this program to return an additional
10 output parameters?  At this point, let's say I already have a change
management application that helps me promote my code changes from
development to test/QA to production.  Typically, this change management
application simply overlays the old object with the new, right?  Well,
what happens if I need to be able to deploy multiple versions of this
program throughout a development life cycle?  How do I keep this old
copy in a deployable state while also making changes and creating new
versions of it over time?

If you're only adding new parameters to the end, you can do that without breaking backward-compatibility.


Use options(*nopass) on the prototypes (no special keywords are necessary if you're using the old CALL/PARM method).

Then use the %PARMS BIF to find out how many parameters were passed. Don't touch the parms that weren't passed, and it'll remain compatible.

For example:

     if %parms >= 51;
        eval PARM51 = DBFIELD51;
     endif;
     if %parms >= 52;
        eval PARM52 = DBFIELD52;
     endif;

And so on...

On the other hand, if you're going to do anything more sophisticated than that, I strongly urge you to stop using programs for this and start using service programs instead.

With service programs, you can protect yourself using binder language. For example, let's say you've got a subprocedure that calculates the price for a product you sell, using it's item number, and the customer's item number:

     P Price_calc      b                   export
     D Price_calc      pi             1N
     D   ItemNo                       5P 0 value
     D   CustAcct                     4P 0 value
     D   Price                        5P 2
      /free
         chain CustAcct PRICEZONE;
         if not %found();
            SetError(PRICE_ERROR_CUSTNF
                    :'Customer not found in Price Zone file');
            return FAIL;
         endif;

         chain (ItemNo: PrZone) PRICES;
         if not %found();
            SetError(PRICE_ERROR_ITEMNF
                    :'Item not found in PRICES file.');
            return FAIL;
         endif;

         price = prPrice;
         return SUCCESS;
      /end-free
     P                 E

Your binding source for the service program look slike this:

     STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('PRICER4 V1.00   ')
        EXPORT SYMBOL(Price_Calc)
        ... other exports here ...
     ENDPGMEXP

Then management decides that you need to be able to sell more expensive items. 5P 2 is no longer a large enough field for prices for these new items. But, you don't necessarily need to break compatibility for programs that are already working, since 99% of your products will still work (or something like that, I'm having a hard time coming up with a good example situation)

You can now add a 2nd subprocedure that works the new way:

     P Price_calc_v2   b                   export
     D Price_calc_v2   pi             1N
     D   ItemNo                       5P 0 value
     D   CustAcct                     4P 0 value
     D   Price                        7P 2
       ... code is the same as previous example, but Price
           is now 7P 2...
     P                 E

And you can change the existing Price_Calc routine to look like this, instead:

     P Price_calc      b                   export
     D Price_calc      pi             1N
     D   ItemNo                       5P 0 value
     D   CustAcct                     4P 0 value
     D   Price                        5P 2

     D tempPrice       s              7P 2
     D RC              s              1N
      /free

          RC = Price_calc_V2(ItemNo: CustAcct: tempPrice);
          if (RC = FAIL);
              return FAIL;
          endif;

          Price = *HIVAL;
          if (tempPrice > Price);
             SetError( PRICE_ERROR_TOOHIGH
                     : 'This routine can''t handle prices above '
                     + %char(Price) + ' use V2 instead!');
             return FAIL;
          endif;

          Price = tempPrice;
          return SUCCESS;
      /end-free
     P                 E

Now you've still got only one version of the code that calculates the price, but existing programs can call the old one without any breakage. New programs can call the V2 version and get the enhanced functionality.

The new symbol must be added to the END of the binder language source:

     STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('PRICER4 V1.00   ')
        EXPORT SYMBOL(Price_Calc)
        ... other exports here ...
        EXPORT SYMBOL(Price_Calc_V2)
     ENDPGMEXP

However, sometimes you want to keep the same names, rather than adding a "V2" to the end or something. That can also be done with binding language & service programs.

Instead of creating a new procedure called "V2", make the new procedure be called "Price_Calc" and the OLD procedure called "Price_Calc_OldV1". (Otherwise, the code would be the same as above.)

Then make your binding language look like this:

     STRPGMEXP PGMLVL(*CURRENT) SIGNATURE('PRICER4 V1.01   ')
        EXPORT SYMBOL(Price_Calc_OldV1)
        ... other exports here ...
        EXPORT SYMBOL(Price_Calc)
     ENDPGMEXP
     STRPGMEXP PGMLVL(*PRV) SIGNATURE('PRICER4 V1.00   ')
        EXPORT SYMBOL(Price_Calc)
        ... other exports here ...
     ENDPGMEXP

You see, subprocedures in a service program are referenced by a number. The first one listed in the binding source is #1, the second is #2, etc. So, anything that's bound today will use the *CURRENT program level. Anything that was bound when the signature was still "V1.00" will use the *PRV signature with that value.

The old programs will call export #1, which is (currently) Price_Calc_OldV1, but they'll reference it as Price_Calc... New programs will call Price_Calc which is export #2 (plus whatever else is in the middle) and so they'll call the new routine.

Personally, I think that method is a lot harder to follow -- but it's an option, and does exist.

Finally... the other advantage that service programs have is that you can force them to be considered obsolete.

If you did it with a prgoram instead, it would likely overwrite memory that you don't have permission to. Give you an invalid value in the returned variable, possibly causing data in your database, on your orders, etc to be corrupt.

By contrast, with a srvpgm, you could you can remove that *PRV section for V1.00 -- now any prgoram that's still calling it the old way will get a "Signature Violation", rather than just a corrupted value, and you'll know that it needs to be fixed (this is analogous to a "level check" on a file)

In order for that to work, you have to make sure that you change the signature each time. (It doesn't consider parameters if it automatically generates it for you, so make sure you set it manually if you need to.)
but it's not a lot of work, and makes your software more stable and more compatible.


HTH



As an Amazon Associate we earn from qualifying purchases.

This thread ...

Replies:

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

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

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