|
I think both solutions are viable, depending on the situation.
Return a pointer for the caller's ability to do things with the data faster
and more directly, return a "handle" to dumb-down the interface and do all
the work for the caller.
I find most programmers in this market do use any helper functions
anyway--they love reinventing the wheel--so they will either (A) use your
interface, (B) wrap it in their own addition code, or (C) write their own
version. Most people choose (C) unfortunately. In addition, most people
continue to use CALL/CALLB for APIs rather than use the prototyped call.
This too is very disappointing.
I have a routine called "Get Pointer to User Space" (the name isn't
important. But most people will still call the QUSCRTUS and QUSRPTRUS APIs,
as follows:
*********************************************************
** C R E A T E U S E R S P A C E
*********************************************************
D QusCrtUsrSpace PR ExtPgm('QUSCRTUS')
D UsrSpace 20A Const
D ExtAttr 10A Const
D nSize 10I 0 Const
D InitValue 1A Const
D PubAuth 10A Const
D szTextDesc 50A Const
D Replace 10A Const
D api_error Like(xt_QUSEC) OPTIONS(*NOPASS)
D bSysDomain 10A Const OPTIONS(*NOPASS)
*********************************************************
** R E T R I E V E P T R to U S E R S P A C E
*********************************************************
D QusRtvPtrToUserSpace...
D PR ExtPgm('QUSPTRUS')
D szUserspace 20A Const
D pRtnPtr *
D api_error Like(QUSEC) OPTIONS(*NOPASS)
What my wrapper procedure does is create the user space (if it doesn't
already exist) and then retrieve the pointer to the user space. So you call
the wrapper/add-in procedure as follows:
Eval pArray = CrtUserSpacePtr('QTEMP/MYUSRSPC')
The prototype is as follows:
**************************************************************
** C R E A T E U S E R S P A C E G E T P O I N T E R
**************************************************************
D CrtUserSpacePtr PR * ExtProc('CRTUSRSPCPTR')
D szUserSpace 21A Const OPTIONS(*NOPASS:*OMIT)
D nInitSize 10I 0 Const OPTIONS(*NOPASS:*OMIT)
You call CrtUserSpacePtr and it will create the user space unless it already
exists, and then return the pointer to that user space. If you don't pass in
a user space name, then it creates one in QTEMP and returns a pointer to
that user space. A random name is generated to insure uniqueness. Once you
have the pointer, assign it to the pointer variable on which your "dynamic"
array is based, and you're done. The caller really doesn't need to worry
about the pointer any more since user spaces created with CrtUsrSpcPtr() are
automatically increased. (Just be sure to use the new V5R3 %SUBARR()
built-in when accessing or sorting a subset of the array's elements.
-Bob Cozzi
www.RPGxTools.com
If everything is under control, you are going too slow.
- Mario Andretti
-----Original Message-----
From: rpg400-l-bounces@xxxxxxxxxxxx [mailto:rpg400-l-bounces@xxxxxxxxxxxx]
On Behalf Of Alan Campin
Sent: Wednesday, July 06, 2005 12:49 PM
To: RPG programming on the AS400 / iSeries
Subject: RE: Multi Occurrence Data Structure returned from a procedure
>> Sure, it would be possible to return a numeric value that provided a
reference to an index, which provided a >> >> reference to an "instance",
but returning the "instance" pointer is a simpler solution.
It might be simpler but I don't agree it is better. Like I indicated in the
previous post, have the user assign a name to the instance and reference it
by the name. A little more work in the service program but a hell of lot
less dangerous for the user of the function. That pointer you are returning
can go anywhere. It is completely type less and can be pointed to anything.
If you assign a name, you can verify the name exists and is valid. Pointer
could be from anywhere.
I have a program I am working on that does dynamic array management. I am
implementing differently but just having the user pass a name to me to
create the array. Internally, I just need a few extra lines of code to
manage the list since you could have multiple arrays open. User just passes
name and I get from list. I have to manage a list with information from the
array anyway. Why not just add the name to the list?
Anyway, my opinion only. I just feel there is always an alternative.
-----Original Message-----
From: Nathan Andelin [mailto:nandelin@xxxxxxxxx]
Sent: Tuesday, July 05, 2005 8:40 PM
To: RPG programming on the AS400 / iSeries
Subject: RE: Multi Occurrence Data Structure returned from a procedure
Scott,
I'm with you on this one. You've described a technique that I've used a
lot, and found to be very practical.
Consider the example of a service program that exports a set of procedures
for managing linked lists, which some folks call dynamic arrays. One
procedure could instantiate a new list, and return a pointer to that
"instance".
The instance pointer could be passed to other procedures used to add or
remove entries in the list. The list "instance" is a reference to a
dynamically allocated data structure, having sub-variables that specify the
number of entries in the list, the pointer to the first entry in the list,
and so forth.
Sure, it would be possible to return a numeric value that provided a
reference to an index, which provided a reference to an "instance", but
returning the "instance" pointer is a simpler solution.
Nathan M. Andelin
Scott Klement <rpg400-l@xxxxxxxxxxxxxxxx> wrote:Hi Alan,
> Returning pointers from a service program or external routine is
> extremely bad form no matter what. The only time that I find it
> appropriate is where it must be returned as with a
> USPC_GetPointerToUserSpace.
I agree with you that you should not return pointers in situations where
the calling routine is expected to interpret the contents of the memory
that it points to.
However, by the same token, I don't think returning a pointer to a user
space is a good idea, either!! The calling program shouldn't need to know
that you've stored your data in a user space, or dynamic memory, or a
file, or anything else -- all of that should be transparent to the caller!
Returning a pointer to a user space is every bit as bad as returning a
pointer to allocated memory or to a static variable.
Unfortunately, in the case of MODS, there's no other way to pass them from
one procedure to another. So, if you MUST pass a MODS, that's how you have
to do it. Though, I agree that replacing the MODS with multiple calls to
a procedure is better, if it's possible.
The one place that I do allow returning a pointer is when the pointer is
"opaque". In other words, the caller doesn't need to know what it points
to, what the format of that space is, how big it is... it doesn't have to
know anything about it. Although I know that some people on this list
won't agree with me, I don't think an opaque pointer is that bad. It's
certainly better than returning a data structure (which is the workaround
that I've seen many people do)
For example, let's say you're creating a service program that works with
an order. You want to use a pseudo-object-oriented approach. So you do
something like this:
x = order_new();
order_load(x: 'SA12354');
order_setShipTo(x: 'Scott Klement'
: '123 Sesame St'
: 'Milwaukee'
: 'WI'
: '53201' );
order_save(x);
order_destroy(x);
In the code above, X is a pointer. The "ORDER" service program contains
subprocedures called "order_new" "order_load" "order_setShipTo", etc.
Inside that service program, it points to a data structure that contains
all of the per-order state information. That way, the srvpgm can be
written to allow many different "order objects" to be created at once.
To encapsulate this, order_new() allocates space for the DS, and returns
the pointer. The caller doesn't know what the format of the memory, the
size of the allocatation, etc are. All it knows is that this is a "handle"
that it has to pass back to the other routines in the order srvpgm.
Obviously, order_destroy() frees up that memory.
That way, if I need to add more stuff to the ORDER srvpgm's data
structure, I can make the allocation bigger, add fields, etc, without
breaking anything.
I consider this approach to be acceptible. Now, I know someone is going
to reply to this message and tell me that I should use a numeric handle
instead of a pointer. And, I don't disagree, but I don't think the
pointer method is bad, either. If someone is foolish enough to examine
the memory that the pointer points to and try to reverse engineer the data
structure, they deserve their fate when an upgrade breaks compatibility :)
But, for sure, in a situation like yours where people are returning a
pointer and expecting the caller to interpret the memory, I think that's a
bad idea, and should be nipped in the bud.
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2025 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.