the basics of prototypes - virgilio.it

28
Your Partner in System i Education Jon Paris Jon.Paris @ Partner400.com www.Partner400.com SystemiDeveloper.com The Basics of Prototypes The authors, Jon Paris and Susan Gantner, are cofounders of Partner400, a firm specializing in customized education and mentoring services for AS/400 and iSeries developers. Their joint careers span over 60 years of MIS experience including periods with IBM's Toronto and Rochester Laboratories. Jon and Susan now devote their time to educating developers on techniques and technologies to extend and modernize their applications and development environments. Together they also author regular technical articles for the IBM publication, eServer Magazine, iSeries edition, and the companion electronic newsletter, iSeries EXTRA. You may view articles in current and past issues and/or subscribe to the free newsletter at: IBMSystemsMag.com Feel free to contact the authors at: Jon.Paris @ Partner400.com and [email protected] This presentation may contain small code examples that are furnished as simple examples to provide an illustration. These examples have not been thoroughly tested under all conditions. We therefore, cannot guarantee or imply reliability, serviceability, or function of these programs. All code examples contained herein are provided to you "as is". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED. Have you visited SystemiDeveloper.com? Check it out for the latest and greatest in System i RPG and database education © Partner400, 2005 - 2008 Prototyping - Page 1-2

Upload: others

Post on 22-May-2022

2 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: The Basics of Prototypes - Virgilio.it

Your Partner in System i Education

Jon ParisJon.Paris @ Partner400.comwww.Partner400.comSystemiDeveloper.com

The Basics of Prototypes

The authors, Jon Paris and Susan Gantner, are cofounders of Partner400, a firm specializing in customized education and mentoring services for AS/400 and iSeries developers. Their joint careers span over 60 years of MIS experience including periods with IBM's Toronto and Rochester Laboratories. Jon and Susan now devote their time to educating developers on techniques and technologies to extend and modernize their applications and development environments.

Together they also author regular technical articles for the IBM publication, eServer Magazine, iSeries edition, and the companion electronic newsletter, iSeries EXTRA. You may view articles in current and past issues and/or subscribe to the free newsletter at: IBMSystemsMag.com

Feel free to contact the authors at: Jon.Paris @ Partner400.com and [email protected]

This presentation may contain small code examples that are furnished as simple examples to provide an illustration. These examples have not been thoroughly tested under all conditions. We therefore, cannot guarantee or imply reliability, serviceability, or function of these programs.

All code examples contained herein are provided to you "as is". THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY DISCLAIMED.

Have you visited SystemiDeveloper.com?Check it out for the latest and greatest in System i RPG and database education

© Partner400, 2005 - 2008 Prototyping - Page 1-2

Page 2: The Basics of Prototypes - Virgilio.it

Review - Parameter Passing

When one program calls another no data is passedOnly pointers that contain the addresses of the parametersThese pointers reference the data back in the originating program

Which could be many levels back

In the example below, what is the content of field Part2 after the call ?

D Part1 S 15

C *Entry PList C Parm Part1

C Eval Part1 = *Blanks C Return

D TestData DS D Part1 10 Inz('Test Da ta1') D Part2 10 Inz('Test Da ta2')

C Call 'TEST2' C Parm Part1

TEST2

Caller

These programs demonstrate the danger of mis-specifying a parameter in the called program.This problem can also occur with numeric fields, but these are more likely to be obvious at the time of the call because they may well cause an "explosion" of some kind. With mis-sized character fields the effect may not be noticed until much later. The writer has encountered at least one program where such an error went undetected for over 7 years!

If you have difficulty believing this, try compiling and runing the two programs below.

* Program TEST1 D TestData DS D Part1 10 Inz('Tes t Data1') D Part2 10 Inz('Tes t Data2') C 'In TEST1' Dsply C TestData Dsply C Call 'TEST2' C Parm Pa rt1 C 'In TEST1' Dsply C TestData Dsply C Eval *InLR = *On

* Program TEST2 - Demonstrates effect of mist reating parameters D Part1 S 15 C *Entry PList C Parm Pa rt1 C 'In TEST2' Dsply C Parm Dsply C Eval Part1 = *Blanks C Parm Dsply C Return

© Partner400, 2005 - 2008 Prototyping - Page 3-4

Page 3: The Basics of Prototypes - Virgilio.it

D TaxCalc PR ExtPgm('PX027C')

D GrossPay 8S 2D Allowances 8S 2D Tax 8S 2

C CallP TaxCalc( GrossPay : Allowances : Tax )

Parameter mismatches can become a thing of the past !

The compiler can validate your program callsPrototypes allow the compiler to validate:

The number of parameters andTheir data type and size

Prototypes can accommodate differences in data defi nitionFor example, allowing an integer to be passed when the callee expects a value with decimal places

The new freeform op-code CALLP is used with Prototy pes It means CALL using Prototype (and NOT Call Procedure)

Prototyping Program Calls

One of the really annoying problems with RPG III is that errors with parameter lists are not discovered until run-time. It would be much better if we could have the compiler validate the parameter lists for us. Prototypes were added to the RPG IV language in the V3R2 and V3R6 releases. Although their initial purpose was to support Prototyping of Subprocedures (more on these later) the RPG developers realized that they could be used to provide this support. As you'll see they also provided a number of other capabilities as well.

The type of error that can result from the incorrect use of parameters can often cause RPG programmers sleepless nights and a difficult debugging tasks.For example:

A 20 character field was expected but only a 10 character field was passed.The called program is modified and now moves blanks to the field instead of the 10 character field it used to move in. This results in a corruption of data in the calling program.

A character field is expected but a signed numeric is passed.A situation like this can continue for years since the internal representation of a character "1" is identical to the representation of the value 1 in a signed numeric field. A programmer makes a "simple change" and passes a different numeric parameter. Suddenly the called program starts producing incorrect output. Why?

Because the new parameter while numeric is a packed field and the representation of the digit 1 is now completely different causing the called program to fail to recognize it correctly.

In this section we will look at some of the powerful options that prototypes give us to deal with situations such as these. The main thing to note is that because the compiler is now able to validate parameters on the call statement, errors of the kind described above are very unlikely to occur

Note: A great many people mistakenly believe that prototypes and CALLP relate only to procedures. This is not true. CALLP can invoke either a program or a procedure. As we shall see shortly, which type of call is made depends on keywords on the PR line of the prototype.

© Partner400, 2005 - 2008 Prototyping - Page 5-6

Page 4: The Basics of Prototypes - Virgilio.it

Anatomy of a Prototyped Call

D TaxCalc PR ExtPgm('PX 027C')

D Gross 8S 2

D Allow 8S 2

D Tax 8S 2

Name of the Prototype

The name used by CallP

Name of the actual program called

ParametersNames are

documentary only

C CallP TaxCalc( GrossPay : Allow ances : Tax)

Type and Length

Prototype Name Parameters

Identifies entry as a PRototype

There are two main parts to a prototype

The first line is the PR line itselfThis supplies the name of the program or procedure to be calledIt also marks the beginning of the parameter listThe PR entry goes in positions 24 -25 (the same place you would put DS for a data structure)

The second and subsequent lines describe the parameters in sequenceParameters are identified by a blank in positions 24-25 just as the subfields of a data structure are.The parameter list is terminated by the appearance of any non-blank entry

For example a DS, S, C, PI (more on this later) or another PRIf the program or procedure to be called does not require any parameters - you don't define any!

Note that the names used for the parameters do not match the names of the fields in the CALLPIn fact they could be completely blank and the compiler would be quite happyIt only cares about the number and type of parameters - the names used are irrelevantSome people use a standard whereby the name used in the prototype identifies the type of field

e.g. Currency, Integer, Name, etc.

The CALLP itself is a free-form operation. The parameters are enclosed in parentheses and separated by colons.

If there are no parameters, then the parentheses are omitted. In V5R1 and later releases, an empty set of parentheses can be used instead. This is the preferred method as it makes the intent of the code more obvious and is compulsory in the /Free version of free-form RPG available in V5R1.

© Partner400, 2005 - 2008 Prototyping - Page 7-8

Page 5: The Basics of Prototypes - Virgilio.it

C Call 'PX027C' C Parm GrossPay C Parm Allowances C Parm Tax

Simple Prototype Example

If we are using these field definitions:

Then the following CALL sequence:

Can be replaced by this Prototype and CALLP

D GrossPay 8S 2 D Allowances 8S 2 D Tax 8S 2

D TaxCalc PR ExtPgm('PX 027C')D Gross 8S 2D Allow 8S 2D Tax 8S 2

C CallP TaxCalc( GrossPay : Allow ances : Tax)

In our example the prototype has been hard coded in the calling program. Normally we would expect to see it being /COPY'd in from a source file supplied by the programmer who wrote PX027C. This is good practice since who knows better what parameters the program is expecting?

This approach is going to seem like more work at first glance. It is! But there are two things it is important to remember:

First - you only have to code it once.Second - our aim is not to speed the writing of the code, rather it is to reduce the opportunity for errors to be introduced during maintenance. Since 80% of the effort expended on most programs is in maintenance/enhancements, the extra time pales into insignificance.

Any variance from these rules will be noted and immediately rejected by the compiler. Compare this to a conventional CALL/PARM situation where the compiler would not produce an error, and the brand new bug could well be introduced into production.

On the next chart we will look at how the compiler reports such failures and the notes page describes the specific conditions the compiler checks for.

© Partner400, 2005 - 2008 Prototyping - Page 9-10

Page 6: The Basics of Prototypes - Virgilio.it

D SixteenLong S 16a

D SixLong S 6a

D TestProto1 Pr ExtPgm('TEST' )D TenLongParm 10a

C Callp TestProto1(SixteenLon g)

C Callp TestProto1(SixLong)RNF7535E The type and attributes of parameter 1 do not match those of the prototype.

In the example the prototype specifies a 10 charact er field.The attempt to use a 16 character field will succeed

This is reasonable since no corruption can occurAt least not in any other field!

But if the called program modifies the field the effect will be like a MOVELi.e. The original right hand characters will remain intact !!

However the attempt to use a six character field will failThe compiler will not allow you to put 4 characters "at risk"

Parameter Mis-match

With a prototype in place, any change in the:

Number of parameters or The data type of a parameter

Will result in an error being signaled by the compiler. The actual error is "The type and attributes of parameter 1 do not match those of the prototype".

Numeric Parameters:The characteristics of the field must match exactly (type, size and decimal places).

Character Parameters:

For varying length fields, the maximum length (i.e. the specified field length) must match exactly.Fixed length character fields, on the other hand are a little more flexible. The parameter passed must be at least as large as the length specified. So if a larger field is passed that is OK, but a smaller field will be rejected.

Any variance from these rules will be noted and immediately rejected by the compiler. Compare this to a conventional CALL/PARM situation where the compiler would not produce an error, and the brand new bug could well be introduced into production.

There are compiler options such as the keywords CONST and OPTIONS(*VARSIZE) which can be used to instruct the compiler to relax these rules somewhat. We will deal with them later.

© Partner400, 2005 - 2008 Prototyping - Page 11-12

Page 7: The Basics of Prototypes - Virgilio.it

D TaxCalc PR ExtPgm('P X027C') D GrossAmount 8S 2 CONST D AllowanceAmount 8S 2 CONST D TaxAmount 8S 2 : : :

D Gross S 7P 2 D Base S 6P 2 D PersAllow S 5P 4 D Tax S 8S 2 : : :

C CallP TaxCalc( Gross : Base + PersAllow : Tax)

C* Eval Temp1 = Gross C* Eval Temp2 = Base + PersAll ow C* CallP TaxCalc( Temp1 : Temp2 : Tax )

Accommodating Parm Mismatches

Let the compiler help you - specify the keyword CON STImplies that the called program will not modify the field

i.e. It will treat the parameter as Read-only

Allows the compiler to generate temporary fields when neededFor example if the size and/or type of the parameter don't match exactly

It also allows an expression to be used instead of a variable !

The use of the CONST keyword allows the compiler to accommodate mismatches in the definition of the parameters between the callee and the caller.

For example, the program we are calling expects a packed decimal value of five digits with no decimal places, but the field we would like to use as a parameter is a three digit signed numeric.Normally we would have to create a temporary variable (packed - five digits) and move the three digit number to it. We would then pass the temporary field as the parameter. In fact the PARM op-code provides this support though the use of Factor 2. When you use the CONST keyword, you are telling the compiler that it is OK for it to make a copy of the data prior to passing it to accommodate such mismatches. This avoids the need for us to explicitly define a working variable of the correct size and type.

The compiler can accommodate differences in:Size, decimal places, and type (zoned, packed, integer, etc.) - for Numeric fields The date format - for Date fields and Length and type (fixed or varying) - for Character fields

Another benefit of using CONST is that it also allows an expression to be passed as a parameter.e.g. Count + 1

© Partner400, 2005 - 2008 Prototyping - Page 13-14

Page 8: The Basics of Prototypes - Virgilio.it

D TaxCalc PR ExtPgm('P X027C') D 8S 2 CONST D 8S 2 CONST D 8S 2 : : :

D Gross S 7P 2 D Allowances S 8S 2 D GrossPay S 8S 2 D Tax S 8S 2

C CallP TaxCalc( Gross : Allow ances : Tax )

C CallP TaxCalc( GrossPay : Al lowances : Tax)

Read-only Parameters

The CONST keyword is a statement of Read-only inten tThere is no guarantee that the called program will honor that intentTemporary fields are only generated when needed

To guarantee Read-only define a Procedure Interface (PI)Only then can the compiler ensure that the request is honoredThe PI replaces the *ENTRY PLIST

One thing you have to be aware of is that the use of the keyword CONST in the prototype in the calling program is only a statement of intent by the calling program, promise - not a guarantee. The called program could still change the value of the field. You can only guarantee the read-only status of the parameter by also coding the CONST keyword on a Procedure Interface (PI), we will look at how to do this next. The use of the CONST keyword differs from using Factor 2 on a PARM op-code in that:

When Factor 2 is used, the value in that field is always copied to the result field before the call is madeWhen CONST is used, the parameter is only copied if there is a mismatch in the parameter

Using the Factor 2 approach requires either that the programmer code a separate CALL/PARM combination for each field to be used as the parameter or hand code a move for each such field into the parameter field.

While this may not seem like a big deal, any extraneous logic (such as Moves just to set up a parameter) obscure the real intent of the code and make it far harder for a maintenance programmer to understand what is going on.

© Partner400, 2005 - 2008 Prototyping - Page 15-16

Page 9: The Basics of Prototypes - Virgilio.it

/Copy TaxCalcPr D TaxCalc PI D GrossPay 8S 2 CONST D Allowances 8S 2 CONST D Tax 8S 2

D TotalPay S 10P 2

C Eval TotalPay = GrossPay + Allowances

C Eval GrossPay = 0

CONST means Read-only so compiler will issue an err or if:Any data is moved to a parameter described as CONST

Or any attempt is made to use** the address of such a parameter

/Copy TaxCalcPr C CallP TaxCalc( Gross : Base + PersAllow : Tax)

PX027C

Using a Program PI with CONST

D TaxCalc PR ExtPgm('P X027C') D 8S 2 CONST D 8S 2 CONST D 8S 2

Caller

TAXCALCPR

If we replace the *ENTRY PLIST by placing a Procedure Interface and Prototype in the called program, the read-only intent will be guaranteed by the compiler.

The compiler will issue an error if:We attempt to modify the content of the field directly We attempt to obtain its address (i.e. using %Addr)

The reason for this is that once we have the address of a variable we can base another field on it and change the contents indirectly

We attempt to pass the field as a parameter to another program or procedure where it is not defined as CONST

This is effectively the same situation as with %Addr - it is just a little less obvious** Note: In early releases, even an attempt to test the address of a parameter against *Null to determine if it had been passed was rejected by the compiler as an error. This overly defensive action was rectified in V4R4 and such testing can now be performed.

© Partner400, 2005 - 2008 Prototyping - Page 17-18

Page 10: The Basics of Prototypes - Virgilio.it

OPTIONS(*NOPASS)Specifies that the parameter is optional

If any parameter is specified as *NOPASS all subsequent parameters in that prototype must also be *NOPASS

Use %Parms to determine if a parameter was passed o r notContains the same value as *PARMS in the PSDS but is easier to use

Optional Parameters

D TaxCalc PR ExtPgm('P X027C') D GrossAmount 8S 2 CONST D AllowanceAmount 8S 2 CONST D TaxAmount 8S 2 D TaxCode 1A OPTIONS(* NOPASS)

...... In program PX027C test for presence of Tax c ode like this ......

C If %Parms > 3 // Tax code is present C Else // Use default value for tax code C EndIf

Optional parameters provide a simple method for extending the functionality of a program (or procedure/function) without the need to modify every program that calls it.

In the example shown here, imagine that we need to modify the tax calculation program so that we can specify a tax code to control the calculation. If we make the parameter optional, then programs that do not need to supply the additional parameter (because for them tax is always calculated the original default way) do not need to be modified. Only those programs that need the new option have to specify it.

Within the called routine, %Parms is checked to determine if the optional parameter was passed or not. If it was not passed, the logic can perform the calculation using the default tax code. If it is passed the supplied tax code is used.

© Partner400, 2005 - 2008 Prototyping - Page 19-20

Page 11: The Basics of Prototypes - Virgilio.it

OPTIONS(*OMIT) The parameter can be omitted

The special value *OMIT is used on the CALLP in place of the parameter

OPTIONS(*VARSIZE)Applies to character fields and arrays passed by reference onlyAllows the parameter to be smaller than specified in the prototype

OPTIONS(*TRIM)Applies to CONST and VALUE character parametersAutomatically strips leading and trailing blanks when copying the field

Other OPTIONS*STRING

Used when prototyping C functions - more on this later

*RIGHTADJ (valid only with CONST or VALUE)Similar to EVALR, right adjusts the sending field's content in the parameter

Other Parameter Options

The OPTIONS parameter and its associated keywords provide some powerful additional facilities.

The option *OMIT can be used for parameters which are not mandatory but which occur in the middle of a parameter sequence and therefore cannot be designated as *NOPASS

When the option is specified the special value *OMIT is allowed for that parameter on the callNote that the parameter will still count in the number of parameters passed and the called program will need to test to see if the parameter was actually passed. Any attempt to reference the parameter when *OMIT was passed will result in an error. There are two ways to do this:

Compare the %Addr of the parameter to *Null. This cannot be used if the parameter was designated as *CONST.Use the API CEETSTA (The RPG Programmers Guide provides a brief example.)

*OMIT is only allowed for parameters that are passed by reference, including those with the CONST keyword.

Option *VARSIZE gives the compiler permission to accept a character parameter that is shorter in length than the prototype specifies

This option is often used when the length of the passed field is also passed to the called program (for example QCMDEXC does this)

Option *TRIM is used with CONST and VALUE parameters and will cause the leading and trailing blanks to be stripped from the field when it is copied into temporary storage. It achieves the same effect as specifying the parameter as %Trim(parmName)

Option *RIGHTADJ is similar to EVALR. Use it when the receiving program/procedure expects a right adjusted string. Note that it must be used with CONST or VALUE. In other words the original content of the sending field will be right adjusted when it is being copied to the temporary parameter that is created.

© Partner400, 2005 - 2008 Prototyping - Page 21-22

Page 12: The Basics of Prototypes - Virgilio.it

Example Prototype for QCMDEXC

This Prototype allows you to:Use a more meaningful name - OvrDBFileUse a Variable size parameter - Options(*VarSize) Avoid having to move values to 15,5 fields - Constand omit parameters - Options(*NoPass)

D CusMastOvr S 40 Inz('OVRDBF FILE(C ustMast) ........

D OvrDBFile PR ExtPgm('QCMDEXC')

D CmdString 3000 Options(*VarSize) Const D CmdLength 15P 5 Const D CmdOpt 3 Options(*NoPass) C onst

OvrDBFile(CusMastOvr: %Len(CusMastOvr)); // You can even build the command like this ... OvrDBFile('OVRDBF FILE(' + FileName +') TOFILE(' + NameToUse + ')' // ... but calculating the length parameter is a problem!

This example of a prototype for QCMDEXC demonstrates a number of the additional benefits of prototyping.

First it allows us to use a name indicative of the function performed - in this case OvrDBFileSecond by using Options(*VarSize) we have made it possible to pass the 40 character parameter CusMastOvr when the prototype specifies that the length should be 3,000 characters long. That's 2,940 bytes saved!Third we don't have to create a 15,5 work field just so that we can pass an integer length!

Has that ever struck you as being rather odd?. If you don't know why QCMDEXC has this rather strange requirement and want to know, ask your instructor.

Lastly we can safely omit the third parameter You did know that QCMDEXC has three parameters didn't you?If you didn't, don't feel bad - the third one is only used for DBCS (Double Byte Character Set) support.

© Partner400, 2005 - 2008 Prototyping - Page 23-24

Page 13: The Basics of Prototypes - Virgilio.it

What Else Can Prototypes Do?

They are the key to many APIs shipped with the AS/4 00The C function library

Which includes:Random number generation Math functions (Sin, Cos, Tan, etc.)MI instructions (MI programming in RPG!)

Not to mention direct access to IFS filesRead and write IFS files, process directories, etc.

And much more. . .

TCP/IP functionsYou can use TCP/IP Sockets functions directly from RPG

ILE APIsFor Dynamic Memory, Error Handling and more

System APIsMost new system APIs are written with the C programmer in mind

Prototypes can also be used to call procedures in other languages, most notably C. This means that RPG IV programs now have access to all the functions in the C function library, which is shipped as part of OS/400 on all systems. Other system APIs, previously available only to C programmers, such as TCP/IP sockets and direct program access to the IFS, are also enabled by the prototyping support associated with subprocedures.

Referring back to the QCMDEXC example, you might be interested to know that through the power of prototyping the C "system" function is available. Some people prefer this to using QCMDEXC in that it allows a more direct determination of the sucess/failure of a command. More on this later.

© Partner400, 2005 - 2008 Prototyping - Page 25-26

Page 14: The Basics of Prototypes - Virgilio.it

Using Simple C Math Functions

Most C routines expect parameters passed by valueSpecified by the keyword VALUEThe actual content (i.e. the value) of the parameter is passedSimilar to CONST but can only be used when calling procedures.

The content of the parameter can be changed by the callee

Here are the prototypes for Sine and Cosine Both return a double length floating point value (8F)C defines them as: double sin(double x) and double cos(double x)

D Sine PR 8F ExtProc('sin') D Double 8F Value

D Cosine PR 8F ExtProc('cos') D Double 8F Value

* These can now be used just as if they were buil t into RPG IV.

C Eval SineX = Sine(X) /Free CosineY = Cosine(Y);

C math functions include acos, asin, atan, cos, cosh, sin, sinh, tan, tanh. All accept double floating point parameters (8F in RPG IV) passed by value, and all return a double floating point value.

It may seem to you that these kinds of math functions are rarely used in RPG programs, and you are right. However, on those occasions when they are needed then you will really appreciate the fact that you don't have to try and "fake" the function out in RPG.

The RPG Redbook (referenced later in this handout) includes an example of these C functions in action.

Don't be put off by the fact that these functions require floating point parameters and return floating point values. You do not need to define floating point variables in order to use them! You can use any type of numeric field. This is possible because the VALUE keyword (like CONST) causes a copy to be made of the data when there is not an exact match but where the base data type (i.e. character or numeric) matches. So if we pass a packed numeric, the compiler will convert it to float before calling the function. Similarly when the result is returned, it will be converted from float to whatever type of numeric field it being assigned to. So - the only place that you need to define floating point is in the prototypes themselves!

© Partner400, 2005 - 2008 Prototyping - Page 27-28

Page 15: The Basics of Prototypes - Virgilio.it

Is that all there is to it ?

Not quite - there are two more things we need to do

First we must specify that the program is a "real" ILE oneWe do this by specifying DFTACTGRP(*NO)

Second we must bind the C library functions to our programThe easy way is to specify QC2LE as the Binding Directory

This saves us from having to worry about which Service Program(s) contain the functions we need.

Embedding the parameters on the H-spec works wellYou won't forget them that way !You can also specify an Activation Group if you wish

Otherwise the default will be QILE

H BndDir('QC2LE') DftActGrp(*No)

C function calls are bound calls and can only be used from "real" ILE programs, as opposed to the OPM compatible programs produced by DFTACTGRP(*YES). So we have to specify DFTACTGRP(*NO)

Note that if you are compiling the program with the CRTBNDRPG command, when you first prompt the command you will not see the Binding Directory parameter. This will not appear until you have entered *NO for the DFTACTGRP parameter - not even if you press F10.

IBM supplies a Binding Directory for each compiler. These are used to allow the Binder to locate the required run-time routines. The compiler automatically passes the name of this Binding Directory to the Binder, there is no need for the programmer to specify it.

The C compiler operates in a similar fashion. The name of its Binding Directory is (you guessed it!) QC2LE. By specifying that Directory we enable the Binder to link us to the C run-time library as well as the RPG one. In V6R1 IBM have incorporated all of the references in QC2LE in the system binding directory - so this requirement should disappear in that release - but it shouldn't cause any problems to leave it in place.

If we want to run the program in an Activation group other than QILE we could also specify the ACTGRP parameter on the H spec.

© Partner400, 2005 - 2008 Prototyping - Page 29-30

Page 16: The Basics of Prototypes - Virgilio.it

Options(*String)

Simplifies the usage of C functions that take strin g parametersOften used together with the BIF %STR

C strings are variable in length and null terminate dBy "null terminated" we mean that the last valid character is followed by hex zeros (i.e. X'00')

This option allows you to pass a regular RPG fixed length fieldThe compiler takes care of the null termination etc.

D Command S 40A Inz('AS/400 command goes here')

....................................

D CallSystem Pr 10I 0 ExtProc('sys tem') D CmdString * Value Option s(*String)

....................................

Reply = CallSystem(Command);

Many published examples of using C type functions fail to use prototyping, or fail to use it correctly and you will often see code like the following:

D Command S 40A Inz('AS/ 400 command goes here') D TempCommand S 41A

*.................................... D CallSystem Pr 10I 0 ExtProc( 'system') D CmdString * Value

*.................................... C Eval TempCommand = %T rim(Command) + X'00'

C Eval Reply = CallSyst em(%Addr(TempCommand))

As you can see this is similar to our example above - but requires more coding and the use of a temporary variable. Since Options(*String) was introduced in V3R7 - only one release after it became possible to call C functions at all it is surprising that some many "wrong" examples are out there. Our take is that if the compiler writers give us a better solution - we use it!

© Partner400, 2005 - 2008 Prototyping - Page 31-32

Page 17: The Basics of Prototypes - Virgilio.it

Options(*String) in action

The C "system" API - an alternative to QCMDEXCIt gives you a simple way to test if the operation was successful

The return value is non-zero in the event of failure

You can also determine the message was that issuedIt is contained in the IMPORTed field _EXCP_MSGID

But this is the only information available

D Reply S 10I 0 D Command S 40A Inz('AS/400 command goes here')

D CallSystem Pr 10I 0 ExtProc('sys tem') D CmdString * Value Option s(*String)

D ErrorCode S 7 Import('_EXC P_MSGID')

/Free Reply = CallSystem(Command);

If Reply <> 0; Dsply ('Reply was ' + %Char(Reply)); Dsply ('_EXCP_MSGID = ' + ErrorCode); EndIf;

Note here the use of the Options(*String) on the prototype. This allows us to use a conventional RPG string as the parameter to the function and compiler generated code will take care of copying it to a null-terminated string and passing a pointer to that string to the C function.

The returned value is a four byte integer (10 I) which will be set non-zero in the event of an error.

If there is an error, the field ErrorCode will contain the conventional 7 character AS/400 message code. This is achieved by mapping the field through the keyword Import and specifying the C name for the field. i.e. Import('_EXCP_MSGID')

C programmers often use and test the results of a function like this in one step. In our example, we are reporting the actual value of the reply code and therefore need to invoke the function and test the result separately. If this was not a requirement (and it normally isn't) the two lines:

Reply = CallSystem(Command);

If Reply <> 0;

Could have been replaced by the simple:

If CallSystem(Command) <> 0;

Note: The %Char function works at all releases V4R4 and later, for earlier releases you need to change the program to replace the use of %Char for editing the numeric field. In V4R2 and V4R3 you can use a combination of %Trim with %EditC/%EditW or other suitable technique if you need it. The function is cosmetic in this code anyway so you can just leave the whole statement out.

© Partner400, 2005 - 2008 Prototyping - Page 33-34

Page 18: The Basics of Prototypes - Virgilio.it

IFS Directory Processing

Three APIs involvedopendir, closedir, readdir

You can probably guess what each of them does!

Use them to "walk" through the directory structure to identify files

D RC S 10I 0D pDir S *

D opendir PR * ExtProc('open dir')D * Value Option s(*String)

D readdir PR * ExtProc('read dir')D * Value

D closedir PR 10I 0 ExtProc('clos edir')D * Value

D DirEnt DS Based( pDirEn t )D ....... Full code for this DS is shown D ....... on the notes pageD Len 10u 0D FileName 640a

D DirEnt DS Based( pDirE nt ) D Res1 16a D GenId 10u 0 D FileNo 10u 0 D RecLen 10u 0 D Res3 10i 0 D Res4 6a D Res5 2a D NLSinfo 12a D CCSID 10i 0 Overlay(NLSi nfo) D CountryId 2a Overlay(NLSi nfo:*Next) D LanguageId 3a Overlay(NLSi nfo:*Next) D Res6 3a Overlay(NLSi nfo:*Next) D Len 10u 0 D FileName 640a

This is the full layout of the directory entry DS. As you can see there is additional information present beyond just the name of the file/directory.

© Partner400, 2005 - 2008 Prototyping - Page 35-36

Page 19: The Basics of Prototypes - Virgilio.it

Directory Processing (contd)Start by opening the required directory

Returned Pointer used as a file "handle" for subsequent operationsIt will be null if the requested direcory does not exist

Then use "readdir" to process all the directory entriesEnd of list will be signalled by pDirEnt being equal to *Null

List will include "." and ".." to indicate superior directory entries

pDir = opendir('/Partner400');

If pDir <> *Null; pDirEnt = readdir( pDir );

DoW pDirEnt <> *Null; Dsply %Subst(FileName: 1: 52); // Display fi rst 52 chars of name pDirEnt = readdir( pDir ); EndDo;

RC = closedir( pDir );

Else; Dsply ('Directory not Found'); EndIf;

General Notes:

C style APIs are only documented by IBM for C usage .No IBM supplied RPG Include files provided

Need to obtain the required constants, structures and prototypes

Don't waste time coding your ownDownload what you need from the Internet

Several of the common ones are included in the Redbook source files Bob Cozzi has many on his web site www.RPGIV.com/downloads/Or use the ones supplied with Scott Klement's tutorial (see below)

IFS APIs are documented in UNIX-Type APIs - Integrated File System (IFS) APIs

Currently (V5R2) there are two PDFs - Parts 1 and 2

For excellent free tutorials on using IFS & Sockets functionsGo to www.ScottKlement.com

© Partner400, 2005 - 2008 Prototyping - Page 37-38

Page 20: The Basics of Prototypes - Virgilio.it

For more read the RPG IV Redbook

Who Knew You Could Do That with RPG IV?

A Sorcerer's Guide to System Access and More

SG24-5402

International Technical Support OrganizationRochester, Minnesota

Available now - SG24-5402Go to www.redbooks.ibm.comIncludes worked examples of

TCP/IP SocketsCGI programmingUsing the C function libraryILE Error handlingand much more ....

Also at the Redbooks siteTwo new Redpieces

One on APIs and another on RPG error handling

Read Barbara Morris' piece at:www.opensource400.org/callc.html

The RPG Redbook is a great place to start your studies into the world of C functions. But if you really want to get into the nuts and bolts of the topic, you should read the article by Barbara Morris referenced on the previous page. Barbara is one of the RPG IV compiler architects and really knows this subject inside out.

C Data Type RPG IV Equivalent Number of bytes

int or long 10I 0 with keyword Value 4

unsigned int 10U 0 with keyword Value 4

double 8F with keyword Value 4

char 10U 0 with keyword Value 4

short 5I 0 2

int * 10I 0 16

unsigned int * 10U 0 16

double * 8F 16

char * * with keyword Value 16

void * * with keyword Value 16

(*) * with keywords Value and ProcPtr 16

This table gives the basic mappings between C data types and their RPG IV equivalents. It is excerpted from the RPG Redbook.

© Partner400, 2005 - 2008 Prototyping - Page 39-40

Page 21: The Basics of Prototypes - Virgilio.it

Questions

?You may find the following charts of interest if you wish to sort or search in ways that SORTA and %LOOKUPxx cannot handle

© Partner400, 2005 - 2008 Prototyping - Page 41-42

Page 22: The Basics of Prototypes - Virgilio.it

Other Functions - qsort & bsearch

Sometimes SORTA and LOOKUP are just not enoughThey assume that the array is full

Although %LOOKUP (V5R1) & %SUBARR (V5R3) help solve this problem

Both are inflexible with regard to collating sequence

The C functions qsort and bsearch may supply an ans werThey can process arrays or Multiple Occurrence Data Structures

Including those using dynamic storage

qsort Sorts the data into sequence

bsearchSearches the data using a "binary chop" technique

In both cases you control the sort/search sequencingYou supply an RPG Subprocedure that makes the decisions

We will look at an example in a moment

The RPG Redbook contains a complete worked example

These functions have been in use by RPG programmers for years, so what’s wrong with them?

There are quite a few things as it turns out, for example:They operate directly only on arrays and not on Multiple Occurrence Data Structures (MODS).They operate on the entire array as defined and not on the actual number of active entries.

If the array is not full, the programmer has to make allowance for the "empty" entries at the end of the array. See note** below for recent enhancements that relieve some of these problems.

Because they attempt to operate on the defined maximum number of elements in the array, they are not suitable for use with BASED arrays (they are "based" on a pointer)**.

The storage for these could be allocated by the programmer using the ALLOC and REALLOC operation codes. They are often used to allow the programmer to "grow" the amount of storage allocated to an array as records are added. This avoids the need to reserve a large amount of storage "just in case". Storage is only used as and when needed.

They are somewhat inflexible in terms of the sort/search collating sequence. For example, an array filled with customer names may not sequence "ACME" and "Acme" together since the sort is based on the EBCDIC sequence. In this case, the characters "C" and "c" are not equivalent. The collating sequence of the program could be changed so that SORTA would sequence them together. However, this would also have the effect of changing the behavior of all comparison operations in the program, not just those related to the array in question.

qsort and bsearch give us an easy way of getting past these limitations. The most important thing to note is that you the programmer determine the collating sequence by writing a simple subprocedure which returns a high/low/equal value. For example, this allows you to treat upper case "A" and lower case "a" as being equivalent. SORTA and LOOKUP will not do this without considerable effort on your part. This is only a simple example of the kind of thing you can do. In fact, anything you can code in RPG can be used to determine the sequence

**Note: The %LOOKUP BIF (V5R1) and %SUBARR (V5R3) help to overcome some of the limitations. For example, they allow you to specify the number of active elements in the array. %LOOKUP can also perform a high-speed binary search.

© Partner400, 2005 - 2008 Prototyping - Page 43-44

Page 23: The Basics of Prototypes - Virgilio.it

Prototypes for qsort

Here's the C prototypevoid qsort (void *base, size_t n, size_t size,

int (*cmp) (const void *, const void *) )

This effectively translates into not one but two prototypesThe first to call qsort And the second defines our comparison procedure (*c mp)

D High C 1 D Low C -1 D Equal C 0

* This is the actual qsort prototype - known here as 'SortIt' D SortIt PR ExtProc( 'qsort') D DataStart * Value D Elements 10U 0 Value D Size 10U 0 Value D CompFunc * ProcPtr Value

* This is the prototype for the RPG sort sequ encing procedure D SeqArray Pr 10I 0 D Element1 Like(Arr ay) D Element2 Like(Arr ay)

For non C programmers, there are two somewhat confusing aspects to the qsort function definition.

First is the use of the data type size_t . This is one of a number of data types that C defines as being "implementor defined". That is to say that the writer of the compiler gets to decide how big it is on their system. For the iSeries (as for many systems) size_t is equivalent to an int i.e. in RPG terms it is a 10I.The second problem is the rather confusing definition for the fourth parameter int (*cmp) (const void *, const void *) It is actually a lot simpler than it looks. It means that the fourth parameter to qsort should be a procedure pointer (*cmp) to a user defined function. This function will be responsible for determining which of the two entries passed to us by qsort is the larger. The rest of the definition spells out the parameters to the function will be pointers to the two entries to be compared (const void *, const void *) and specifies that it must return an integer int .

If we were to write the C definition for our comparison routine it would look like this: int ourcompareroutine (const void *, const void *)

If we were to literally translate the prototype for the sequencing parameter to RPG it would look like the example below. However, since this will be a custom routine we can actually take advantage of the fact that passing a parameter by reference is the same as passing a pointer to it by value. As a result we can use the simplified version shown on the main chart.

* This is the "raw" prototype for the sort se quencing procedure D SeqArray Pr 10I 0 D Element1 * Value D Element2 * Value

© Partner400, 2005 - 2008 Prototyping - Page 45-46

Page 24: The Basics of Prototypes - Virgilio.it

How qsort Operates

CallP to qsort passing 4 parmsData to sortNumber of ElementsSize of each elementSequencing Procedure

Sequencing ProcedureProcess A and B as you wishIf A > B return 1If A = B return 0If A < B return -1

qsort

1

3..

4

2..

Both qsort and bsearch are examples of Callback processing. That is to say that after they are invoked, they remain in control, but "call back" into our code for additional processing. Only when their task is complete do they return control to the original caller. In the case of qsort/bsearch the additional processing that our code performs is to provide the sequencing information.

On the following chart we will show a typical sequencing routine for qsort.

To get the prototypes and a full explanation to help you to understand the whole process though, we suggest that you read the section of using C functions in the RPG Redbook "Who Knew You Could Do That in RPG IV" - further details of where to find the Redbook can be found later in this handout.

© Partner400, 2005 - 2008 Prototyping - Page 47-48

Page 25: The Basics of Prototypes - Virgilio.it

// Array uses the record format of the Customer M aster (Custmast) // Among other fields it contains Custname and State

D custData DS LikeRec(Cust Mast) Dim(5000)

// custCount is contains count of active records in custData array D custCount S 5i 0

// Sort the custData array using the subprocedure SEQBYCITY

C CallP SortIt(%Addr(custDat a) C : custCount C : %Size(custDa ta) C : %PAddr('SEQB YCITY') )

Calling qsort

The call looks a little "odd" by RPG standardsPassing the address of a variable (%Addr) is not the normNor is passing a procedure pointer (%PAddr)

But the basic call will look the same every time yo u use it!bsearch is basically the same except there is an additional parameter

The address of the string to search for is added at the beginning of the list

This chart shows the basic call to qsort and the definitions of the primary data items.

Although it is not a typical RPG call - once you understand the basic structure of it, you will find that the call always looks the same. It is really very straightforward.

© Partner400, 2005 - 2008 Prototyping - Page 49-50

Page 26: The Basics of Prototypes - Virgilio.it

P SeqArray B

D PI 10I 0 D Element1 Like(Array) D Element2 Like(Array)

C Select C When Element1 < Element2 C Return Low C When Element1 > Element2 C Return High C Other C Return Equal C EndSl

P E

A Generic Sequencing Routine

A basic sequencing routine for qsort is very simpleA bsearch search routine looks almost identical as you will see

This is all there is to itNot very complicated at all - and the process is always the same !

P SeqRecords B D PI 10I 0 D Record1 LikeRec(Cust Mast) D Record2 LikeRec(Cust Mast) /FREE Select; // State is the primary key so work with that first When Record1.State < Record2.State; Return Low; When Record1.State > Record2.State; Return High; Other; Select; // Compare on City name only when State is equal When Record1.City < Record2.City; Return Low; When Record1.City > Record2.City; Return High; Other; Return Equal; EndSl; EndSl;

Sorting on Multiple KeysSlightly more complicated

But qualified names and LIKREC make it pretty easy

© Partner400, 2005 - 2008 Prototyping - Page 51-52

Page 27: The Basics of Prototypes - Virgilio.it

Searching Using bsearch

// Prototype for bsearch D bsearch PR * ExtProc('bse arch') D searchFor * Value D dataStart * Value D elements 10U 0 Value D size 10U 0 Value D CompFunc * ProcPtr Valu e // Prototype for City search routine D CitySearch PR 10I 0 D searchCity Like(city) D Element LikeRec(Cust Mast)

// Search the custData array using the subprocedu re CITYSEARCH

pcustItem = bsearch(%Addr(city) : %Addr(custData ) : custCount : %Size(custData) : %PAddr(' CITYSEARCH') );

The process is very similar to that used with qsortExcept that we pass an additional parameter

The address of the item that we want to search for

We will however need to do additional processing after the searchMore on this later

// City search subprocedure P CitySearch B D PI 10I 0 D searchCity Like(city) D Element LikeRec(cust Mast)

/FREE Select; When searchCity < element.City; Return Low; When searchCity > element.City; Return High; Other; Return Equal; EndSl;

Search Procedure for bsearch

The search procedure itself is very simpleBut remember that in order for it to work the data must be in sequence

How might you adapt it for case-insensitive compari sons ?Or to search for a specific City/State combination?

© Partner400, 2005 - 2008 Prototyping - Page 53-54

Page 28: The Basics of Prototypes - Virgilio.it

The problem with bsearch

// This is the calculation to determine the eleme nt number distance = elementAddr - startAddr; element = (distance / elementSize) + 1;

If there is more than one element with the same key valueThe pointer returned may not be to the first element with that valueThe same situation applies to any binary search

e.g. That performed by %LOOKUPfor arrays designated Ascend/Descend

If this is important, you must locate the first ele ment yourselfFirst you need to translate the pointer to an element number

Calculate the "distance" between the start of the array and located elementDivide this by the length of an elementAnd then add 1 to the result to get the element number

Then "walk" backwards through the elements to find the first element with the same value

© Partner400, 2005 - 2008 Prototyping - Page 55-56