× 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.



Booth,
I (of course) use CodeStudio and its project manager to stored an
application (i.e, program or service program) and all of its associated
parts. I document all my stuff using HTML (compiled HTML Help actually) with
an index over the procedure names and descriptions. I find this to be most
effective.  
The new WDSc has some stuff in it to show you an outline of a source member
that does list the subprocedures. But that in and of itself doesn't help a
developer who is writing a new piece of code and wants to use an existing
proc to do function X.  
The Microsoft IDE tools not only provide AutoCompletion for their own APIs
and procedures, but for programmer-written procedures as well. Why IBM does
not do this is unclear to me. 
I think we need to start documenting the procedures we build using HTML
helptext and provide easy access to it for our developers. We also need to
keep hammering on IBM to build a better IDE; so far they just keep telling
us how great the next release is going to be, and then when it comes out,
the tell us how great the one after that is going to be... but we never end
up with a good IDE and people end up sticking with SEU/PDM. 


Bob Cozzi
Cozzi Consulting
www.rpgiv.com


-----Original Message-----
From: rpg400-l-bounces@xxxxxxxxxxxx [mailto:rpg400-l-bounces@xxxxxxxxxxxx]
On Behalf Of Booth Martin
Sent: Monday, October 27, 2003 9:39 AM
To: rpg400-l@xxxxxxxxxxxx
Subject: Re: Starting out with sub-procedures

Thank you Simon. Your explanation cleared up a lot of confusion for me.
There are still many questions in my mind but a lot of what has seemed
redundant to me is now washed away.
 
How does one keep track of all these functions and keep them indexed and
available so the consumer/programmer can quickly find them? What schema has
proven effective? Are there recommended groupings? Is there a hierarchy that
seems to have worked well?
 
---------------------------------------------------------
Booth Martin http://www.MartinVT.com
Booth@xxxxxxxxxxxx
---------------------------------------------------------
 
-------Original Message-------
 
From: RPG programming on the AS400 / iSeries
Date: Monday, October 27, 2003 2:11:15 AM
To: RPG programming on the AS400 / iSeries
Subject: Re: Starting out with sub-procedures
 
On Monday, October 27, 2003, at 01:30 PM, Booth Martin wrote:
 
> The question remains: do I need more than a /COPY statement and the
> callp
> line in the main program(s)?
>
 
For a prototyped call to a *PGM object that is all you need to do.
Include the prototype and call the name of the prototype. Thus:
 
/define LONG_PROC_NAMES
/COPY rpgleinc,qcmdexc
 
C CALLP QcaRunClCmd( 'WRKSPLF' : 7 )
 
For a prototyped procedure call the only other thing you need to know
is where to find the procedure because you must bind your program to
the module or service program that contains the procedure. Some
documentation explaining limitations, side effects, valid data ranges,
etc. is helpful but only needed for complex procedures. That sort of
information should be in the /COPY member containing the prototypes as
part of the comment block for each prototype. The consumer includes the
appropriate /COPY member, invokes the procedure, and binds to the
service program or module. Thus:
 
H BNDDIR('QC2LE')
 
/COPY rpgleinc,stdlib
 
C EVAL rc = system( 'WRKSPLF' )
 
> My primary interest is in avoiding knowing any more about a procedure
> than
> the function and the parm(s). I have a strong desire to NOT know who is
> maintaining the code, etc. I don't really even want to look inside the
> code.
> It is none of my business. None whatsoever.
 
That is the whole point of procedures (at least those procedures
intended for general consumption via the public interface of a service
program). All that the consumer of the function needs to know should be
apparent from the prototype. For example, call function A passing X, Y,
and Z and receive R as a return value. The consumer does not, and
should not, care how the procedure does its stuff--only that it does.
Procedures used in this way are black-box building blocks from which a
more complex application can be built.
 
Private procedures (i.e., those that exist within a program or those
that are not part of the public interface of a service program) serve
the same purpose but it can be argued that subroutines also satisfy
this requirement. Procedures have additional benefits when compared to
subroutines:
1) Support for parameters (or arguments depending on your perspective)
2) Locally scoped variables
3) Return values
 
These benefits become apparent only when you engineer software rather
than simply cobbling code together and thus are often apparent only
programmers who appreciate the craft of programming. I would venture
that many RPG programmers do not and programming for them is simply a
means to an end. It pays them more money than they could earn doing
anything else and that keeps a roof over their head and food on their
table.
 
By defining a formal interface to a routine I can ensure that the only
way data gets into the procedure is via the interface--no side effects.
 
By using locally scoped variables I can ensure that my procedure does
not inadvertently change the value of a variable used elsewhere in the
program (global values notwithstanding)--no side effects.
 
By using return values I can make my code more elegant. For example,
 
EVAL R = functionA( X, Y, Z )
 
is cleaner and easier to read than
 
CALLP functionA( R, X, Y, Z)
 
With the first form I **KNOW** R is changed by the function. As a
result I can presume X, Y, and Z are input values and thus probably not
changed by the function. The prototype will tell me for certain. With
the second form I don't know whether any of the values are changed nor
whether all of them are changed. Perhaps the convention is that the
first value is the return value but perhaps not. I would use the first
form when a function returns a single value and the second form when
the function must return multiple discrete values. In general I would
prefer to return multiple value in a data structure even though
returning large character values is less efficient. This allows me to
define procedures with a single return type even if that return type is
a composite structure.
 
Perhaps a worked example is in order? Presume I want to write a
function that returns an uppercase version of an input string. Presume
also that I do not want to impose artificial constraints on the size of
the string the function will handle. Since RPG IV imposes a limit on
the maximum size of a character string (32K prior to 510 and 64K from
510 on) I design my function to accept the address of an input value
and the address of an output location. Since I do not want to impose
length restrictions I must also include variables for the length of the
data. Since this function returns the same amount of data it received
(just uppercased) we can require that the input length and output
lengths are the same. Here is one such prototype:
D toUpperCase PR
D inString * VALUE
D outString * VALUE
D stringLen 10I 0 VALUE
 
The function (or procedure) that implements the prototype would look
like:
P toUpperCase B EXPORT
D toUpperCase PI
D inString * VALUE
D outString * VALUE
D stringLen 10I 0 VALUE
 
* Uppercasing stuff goes here
 
P toUpperCase E
 
Given:
D name S 10 INZ('booth')
D upperName S 10
 
The caller would code:
CALLP toUpperCase( %ADDR(name) : %ADDR(upperName) : %LEN(%TRIM(name))
)
 
Now this prototype is ugly from an RPG IV perspective. It forces the
caller to deal with pointers and determining the length of large
character variables using %TRIM is inefficient.
 
So I create a wrapper for this function that handles the length aspect
automatically and allows the caller to specify variable names rather
than addresses.
 
First I rename the ugly function (using a suffix of _p for pointer).
D toUpperCase_p...
D PR
D inString * VALUE
D outString * VALUE
D stringLen 10I 0 VALUE
 
and I define a new uppercase function:
D toUpperCase PR OPDESC
D inString 32767
D outString 32767
 
The function that implements this prototype would look like:
P toUpperCase B EXPORT
D toUpperCase PI OPDESC
D inString 32767 OPTIONS(*VARSIZE)
D outString 32767 OPTIONS(*VARSIZE)
 
D stringLen S 10I 0
* Call IBM API to determine the string lengths from the operational
descriptors
 
* Complain if the lengths are not the same or just use the shorter
length
 
* Convert the input string to uppercase
C CALLP toUpperCase_p( %ADDR(inString) : %ADDR(outString) :
C stringLen )
 
P toUpperCase E
 
See how we provided a cleaner interface but still reused the _p version
for the implementation. The implementation uses the ugly interface but
the consumer gets to use a cleaner interface.
 
Now the caller can code:
CALLP toUpperCase( name : upperName )
which is much nicer than the first version but it is not really
apparent which variables are input to the function and which are
output. The variable names help but we can improve the interface
further by changing the prototype to:
D toUpperCase PR 32676 OPDESC
D inString 32767 OPTIONS(*VARSIZE)
 
The function that implements this prototype would look like:
P toUpperCase B EXPORT
D toUpperCase PR 32676 OPDESC
D inString 32767 OPTIONS(*VARSIZE)
 
D stringLen S 10I 0
* Call IBM API to determine the string length from the operational
descriptor
 
* Convert the input string to uppercase
C CALLP toUpperCase_p( %ADDR(inString) : %ADDR(outString) :
C stringLen )
 
C RETURN %SUBST(outString : 1 : stringLen)
P toUpperCase E
 
allowing the caller to code:
EVAL upperName = toUpperCase( name )
which is the nicest way possible. It is blindingly obvious what is
input to the function and what is output. It does have the minor
disadvantage of being less efficient than the previous versions due to
it causing 32K to be copied on to the stack and then copied into the
receiving variable. I would rather pay the minor performance cost and
have nice looking code. If I really find the performance an issue then
I can always call the _p version directly.
 
This process can be continued with number of variants. For example:
D toUpperCase_v...
D PR 32767 VARYING
D inString 32767 VARYING
 
D toUpperCase_s...
D PR 32767
D inString * VALUE OPTIONS(*STRING)
 
The caller can either invoke these special variants directly or
prototypes can be used to "hide" the real function. For example, the
/COPY member containing the prototypes might look like:
 
/if defined(USE_VARYING_STRINGS
D toUpperCase PR 32767 VARYING
D EXTPROC(toUpperCase_v)
D inString 32767 VARYING
/elseif defined(USE_C_STRINGS)
D toUpperCase PR 32767
D EXTPROC(toUpperCase_s)
D inString * VALUE OPTIONS(*STRING)
/elseif defined(USE_POINTERS)
D toUpperCase PR EXTPROC(toUpperCase_p)
D inString * VALUE
D outString * VALUE
D stringLen 10I 0 VALUE
/else
D toUpperCase PR 32676 OPDESC
D inString 32767
/endif
 
Thus all callers invoke toUpperCase() but the function really invoked
depends on the type of data being processed. The caller does need to
know what data they will be processing in order to define the proper
prototypes and they cannot use different underlying methods in the same
program without performing some contortions. Obviously it would be much
better if the compiler could determine which function to call based on
the data type of the parameters and that leads us nicely into the
requirement for overloaded procedures.
 
In the real versions of these procedures I would allow expressions to
be specified on the input values but that would just complicate things
here. The binding source for the service program would export the _p,
_s, _v versions in addition to the plain version.
 
Does this help at all or are you more confused than ever?
 
Regards,
Simon Coulter.
--------------------------------------------------------------------
FlyByNight Software AS/400 Technical Specialists
 
http://www.flybynight.com.au/
Phone: +61 3 9419 0175 Mobile: +61 0411 091 400 /"\
Fax: +61 3 9419 0175 \ /
X
ASCII Ribbon campaign against HTML E-Mail / \
--------------------------------------------------------------------

_______________________________________________
This is the RPG programming on the AS400 / iSeries (RPG400-L) mailing list
To post a message email: RPG400-L@xxxxxxxxxxxx
To subscribe, unsubscribe, or change list options,
visit: http://lists.midrange.com/mailman/listinfo/rpg400-l
or email: RPG400-L-request@xxxxxxxxxxxx
Before posting, please take a moment to review the archives
at http://archive.midrange.com/rpg400-l.




As an Amazon Associate we earn from qualifying purchases.

This thread ...

Follow-Ups:
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.