Difference between revisions of "Service Program Signature Violations"

From MidrangeWiki
Jump to: navigation, search
m
Line 2: Line 2:
 
[[Category:Programming]]
 
[[Category:Programming]]
 
You wrote:<br/>
 
You wrote:<br/>
>Maybe I'm missing something obvious ... is there a way to make RPG programs<br/>
+
<blockquote>Maybe I'm missing something obvious ... is there a way to make RPG programs ignore signature violations in service programs?</blockquote>
>ignore signature violations in service programs?<br/>
 
  
Yes.  You can use binding source to force LVLCHK(*NO) which creates a
+
Yes.  You can use binding source to force [[LVLCHK]](*NO) which creates a
signature of zero.  However, like LVLCHK(*NO) on database files, doing so is
+
signature of zero.  However, like [[LVLCHK]](*NO) on database files, doing so is a truly stupid idea.  And since you must create binding source to set a zero signature you might as well learn to use it properly.
a truly stupid idea.  And since you must create binding source to set a zero
 
signature you might as well learn to use it properly.
 
  
 
There was a good discussion on binding source earlier this year.  Search the
 
There was a good discussion on binding source earlier this year.  Search the
 
archives.  However, read on ...
 
archives.  However, read on ...
  
>I'm working on a bunch of service programs, and every time I add a<br/>
+
<blockquote>I'm working on a bunch of service programs, and every time I add a  
>procedure to my service program, I have to recompile all the callers.<br/>
+
procedure to my service program, I have to recompile all the callers.</blockquote>
  
 
I'll bet you are creating the service program with EXPORT(*ALL).  The
 
I'll bet you are creating the service program with EXPORT(*ALL).  The
Line 33: Line 30:
 
signature explicitly.
 
signature explicitly.
  
You must create a member in [[source file]] to hold the [[binding source]].  The
+
You must create a member in [[source file]] to hold the [[binding source]].  The default source file is [[QSRVSRC]] and the default member has the same name as the service program being created.
default source file is QSRVSRC and the default member has the same name as
 
the service program being created.
 
  
 
The binding source contains a [[STRPGMEXP]] command, one or more [[EXPORT]]
 
The binding source contains a [[STRPGMEXP]] command, one or more [[EXPORT]]
Line 42: Line 37:
 
(like a tag language).
 
(like a tag language).
  
The binding source may contain multiple groups of STRPGMEXP, EXPORT, and
+
The binding source may contain multiple groups of [[STRPGMEXP]], [[EXPORT]], and [[ENDPGMEXP]].  Each group is called an export block and defines a different public interface and corresponding signature.
ENDPGMEXP.  Each group is called an export block and defines a different
 
public interface and corresponding signature.
 
  
 
Here is a working example.  I design a service program called STRINGS which
 
Here is a working example.  I design a service program called STRINGS which
Line 72: Line 65:
  
 
There is a tool in [[QUSRTOOL]] called GENBNDSRC which is useful for creating
 
There is a tool in [[QUSRTOOL]] called GENBNDSRC which is useful for creating
the first set of binding source but since it insists on sorting the EXPORT
+
the first set of binding source but since it insists on sorting the [[EXPORT]]
 
statements is completely bloody useless for subsequent changes.
 
statements is completely bloody useless for subsequent changes.
  
If I store the binding source in member STRINGS in file QSRVSRC then when I
+
If I store the binding source in member STRINGS in file [[QSRVSRC]] then when I
 
create my service program the binding source will be processed
 
create my service program the binding source will be processed
 
automatically.
 
automatically.
Line 90: Line 83:
 
break things.
 
break things.
  
<nowiki>I edit the binding source and I duplicate the export block and change one of
+
I edit the binding source and I duplicate the export block and change one of
 
the blocks to PGMLVL(*PRV).  I then add my new procedure the end of the
 
the blocks to PGMLVL(*PRV).  I then add my new procedure the end of the
*CURRENT block.</nowiki>
+
<nowiki>*CURRENT</nowiki> block.
  
 
<pre>        STRPGMEXP  PGMLVL(*CURRENT)
 
<pre>        STRPGMEXP  PGMLVL(*CURRENT)
Line 170: Line 163:
 
branch office would spell it center thus my service program will drive them
 
branch office would spell it center thus my service program will drive them
 
insane by generating binding errors telling them the center procedure cannot
 
insane by generating binding errors telling them the center procedure cannot
be found.  One solution would be to use an RPG IV prototype to map center()
+
be found.  One solution would be to use an [[RPG IV]] prototype to map center() to centre().  A similar thing could be done for [[C]].  However, that doesn't help [[COBOL]] programmers.  So I add a new procedure to STRINGS called center() which simply invokes centre() to do the real work.  I update the binding source accordingly.
to centre().  A similar thing could be done for C.  However, that doesn't
 
help COBOL programmers.  So I add a new procedure to STRINGS called center()
 
which simply invokes centre() to do the real work.  I update the binding
 
source accordingly.
 
  
 
<pre>        STRPGMEXP  PGMLVL(*CURRENT)
 
<pre>        STRPGMEXP  PGMLVL(*CURRENT)
Line 202: Line 191:
 
given program.
 
given program.
  
<nowiki>Binding source does not help with parameter interface changes.  One can only
+
Binding source does not help with parameter interface changes.  One can only
 
hope that will be a future extension.  It would be really good to have
 
hope that will be a future extension.  It would be really good to have
 
overloaded procedures and use binding source to define the parameter types
 
overloaded procedures and use binding source to define the parameter types
 
and number.  Until then we must content ourselves with OPTIONS(*NOPASS) or
 
and number.  Until then we must content ourselves with OPTIONS(*NOPASS) or
*OMIT for extending parameter lists and resign ourselves to the fact that we
+
<nowiki>*OMIT</nowiki> for extending parameter lists and resign ourselves to the fact that we can't pass different data types in the same parameter via prototyped calls.
can't pass different data types in the same parameter via prototyped calls.</nowiki>
 
  
 
<i>Copied from a post by Simon Coulter in RPG400-L</i>
 
<i>Copied from a post by Simon Coulter in RPG400-L</i>

Revision as of 22:04, 10 September 2006

You wrote:

Maybe I'm missing something obvious ... is there a way to make RPG programs ignore signature violations in service programs?

Yes. You can use binding source to force LVLCHK(*NO) which creates a signature of zero. However, like LVLCHK(*NO) on database files, doing so is a truly stupid idea. And since you must create binding source to set a zero signature you might as well learn to use it properly.

There was a good discussion on binding source earlier this year. Search the archives. However, read on ...

I'm working on a bunch of service programs, and every time I add a procedure to my service program, I have to recompile all the callers.

I'll bet you are creating the service program with EXPORT(*ALL). The signature is calculated from the exported items -- procedures and data. If you cheat and use EXPORT(*ALL) when building the service program you will get a new signature whenever you:

  • Add a new procedure
  • Add a new data item with EXPORT
  • Change the name of a procedure
  • Change the name of a data item with EXPORT
  • Change the order of exported procedures or data

A new signature will require that you recompile the calling programs and service programs unless you manage the signature creation.

If you define and use binding source you are controlling the EXPORTed items and therefore controlling the signature -- you can also specify the desired signature explicitly.

You must create a member in source file to hold the binding source. The default source file is QSRVSRC and the default member has the same name as the service program being created.

The binding source contains a STRPGMEXP command, one or more EXPORT commands, and an ENDPGMEXP command. Note that these commands are not runnable -- they merely provide a convenient mechanism for defining things (like a tag language).

The binding source may contain multiple groups of STRPGMEXP, EXPORT, and ENDPGMEXP. Each group is called an export block and defines a different public interface and corresponding signature.

Here is a working example. I design a service program called STRINGS which will contain string handling functions. I initially write three procedures which will be publicly accessible (i.e., callable). They are toUppercase(), toLowercase(), and centre(). I may also have some support procedures which I do not want exposed. They are only for the use of the service program and not to be invoked by any caller. Let us presume I have a getStringLength() private procedure.

I define my public interface using binding source.

        STRPGMEXP  PGMLVL(*CURRENT)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        ENDPGMEXP

Note that the symbol can be a procedure name or a data item name. Note also that the procedure names used here are not enclosed in apostrophes which means the will be interpreted as uppercase names. That is OK because my service program is written in RPG IV which is not case sensitive. If I were writing the service program in C then I must ensure the symbol name is exactly the same as the name in the module and use apostrophes as appropriate. Also note that the getStringLength() function is not mentioned in the binding source.

There is a tool in QUSRTOOL called GENBNDSRC which is useful for creating the first set of binding source but since it insists on sorting the EXPORT statements is completely bloody useless for subsequent changes.

If I store the binding source in member STRINGS in file QSRVSRC then when I create my service program the binding source will be processed automatically.

I compile the programs that use these string functions and life in the IT department grinds slowly on.

Some time later I discover the need for an additional string function for a new program I am creating. I need a SOUNDEX function so I write one and add it to my STRINGS service program. I need to make the getSoundex() function part of the public i�terface. If I simply add it to the existing export block then I will create a new current signature thus breaking any existing programs that use STRINGS. I must follow certain steps to ensure I don't break things.

I edit the binding source and I duplicate the export block and change one of the blocks to PGMLVL(*PRV). I then add my new procedure the end of the *CURRENT block.

        STRPGMEXP  PGMLVL(*CURRENT)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        EXPORT     SYMBOL(getSoundex)
        ENDPGMEXP

        STRPGMEXP  PGMLVL(*PRV)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        ENDPGMEXP

Any new procedures MUST, repeat MUST go at the end of the current block. (The real code can go anywhere in the service program itself.)

Now when I create the STRINGS service program it gets two signatures created. A current one used by any new programs I compile, and a previous one which mathces the one used by the existing programs. Magic happens! Any existing programs that use STRINGS see the previous signature and thus the previous view of the service program exports.

The ILE Concepts manual has a very good discussion on this stuff. You should read it thoroughly before creating binding source.

It is also possible to define your own signature.

        STRPGMEXP  PGMLVL(*CURRENT) SIGNATURE('MY_SIG')
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        ENDPGMEXPM

This is a little dangerous because the system will assume I know what I am doing but it does allow me to be a little more flexible in building a service program in increments. Let us presume I am building the STRINGS service program in stages. My first build use the binding source in the previous paragraph. Other developers start using it. I then add the SOUNDEX function. I don't want another signature because, conceptually, the SOUNDEX function is part of the current version of the service program. I change the binding source as follows:

        STRPGMEXP  PGMLVL(*CURRENT) SIGNATURE('MY_SIG')
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        EXPORT     SYMBOL(getSoundex)
        ENDPGMEXP

This has the same effect as the first example but only one signature was created. I am accepting the responsibility for ensuring that the exports are in the correct order rather than the system (this is similar to having a zero signature but much more flexible).

If I am careless and I define the binding source as

        STRPGMEXP  PGMLVL(*CURRENT) SIGNATURE('MY_SIG')
        EXPORT     SYMBOL(getSoundex)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        ENDPGMEXP

then I have just broken all existing programs that use STRINGS. When an existing program calls toUppercase() it will actually invoke getSoundex(). When it invokes toLowercase() it wil actually invoke toUppercase(). Oops!

Sometimes this behaviour is useful. The ILE Concepts manual has an example showing how to change a rate calculation function for new programs but not upset existing programs. I wouldn't do this sort of thing the way that manual describes but it is interesting in an illustrative way.

One last example. The literate among you will have noticed that I spell centre the correct way. However my cousins (thrice removed) in the American branch office would spell it center thus my service program will drive them insane by generating binding errors telling them the center procedure cannot be found. One solution would be to use an RPG IV prototype to map center() to centre(). A similar thing could be done for C. However, that doesn't help COBOL programmers. So I add a new procedure to STRINGS called center() which simply invokes centre() to do the real work. I update the binding source accordingly.

        STRPGMEXP  PGMLVL(*CURRENT)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        EXPORT     SYMBOL(getSoundex)
        EXPORT     SYMBOL(center)
        ENDPGMEXP

        STRPGMEXP  PGMLVL(*PRV)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        EXPORT     SYMBOL(getSoundex)
        ENDPGMEXP

        STRPGMEXP  PGMLVL(*PRV)
        EXPORT     SYMBOL(toUppercase)
        EXPORT     SYMBOL(toLowercase)
        EXPORT     SYMBOL(centre)
        ENDPGMEXP

Do you see the process? It is quite straightforward if you follow the rules. Now, everyone is happy. Although given the atrocious spelling of most programmers they are just as likely to spell centre both ways in any given program.

Binding source does not help with parameter interface changes. One can only hope that will be a future extension. It would be really good to have overloaded procedures and use binding source to define the parameter types and number. Until then we must content ourselves with OPTIONS(*NOPASS) or *OMIT for extending parameter lists and resign ourselves to the fact that we can't pass different data types in the same parameter via prototyped calls.

Copied from a post by Simon Coulter in RPG400-L