Basic story is we primarily do development in C, but part of the
software is an API in which we can't control the specification. Long ago
when we had someone that did know RPG well, he wrote a sample program
that used this API that has been the basis for other work. The API has
since changed slightly (what was a long is now a pointer to a long) and
the sample no longer works.
Since applications have to adhere to the API, the RPG needs to change.
We now do almost nothing in RPG, but need to keep the few we have going
but the RPG person is no longer around. It seems like a simple change,
but IBM doc is confusing and while samples and articles I found on
Google are better, I have had no success yet.
Below are snips of what seem like the appropriate sections of source
since the actual source is somewhat lengthy. The "ECX" var is the one in
question that was a long and is now a pointer (it is basically used as a
"context" key for multiple connections).
Without changes, it compiles are runs fine with the older version of the
API, but with new one it gets an "RNX1255 unmonitored by xxxx at
statement yyy" message because the value in the var is an address and
not the value and hits some logic that is just plain wrong because it
was never coded to expect this. If we add a "VALUE" option to the ECX
prototypes we no longer get system error, but values expected back
aren't right so error check fails and goes to a fail case. I looked at
code for %ADDR and %STR use and it didn't seem right either, I'm just
confused by this all.
Is there a really good RPG "pointer" person out there that can help or a
sample I can follow that matches this use?
... Control block info (just initial part).
D SCB DS
D ECX 1 4B 0
D SID 5 8B 0
D COMMD 9 20
D OPSTA 21 24B 0
... Various prototypes.
D INIT PR
D ECX 9B 0
D OPSTA 9B 0
...
D CONNECT PR
D ECX 9B 0
D SCB 220A
D XUSER 12A
...
D TERM PR
D ECX 9B 0
D OPSTA 9B 0
... Initialize and get back context
C Z-ADD 0 ECX
C CALLP INIT(ECX :OPSTA )
... Do some error checking and connect if ok
C CALLP CONNECT(ECX :SCB :
C XUSER :LGHID :
C XPASSW :LGHPAS :
C XSERV :LGHSRV)
... When done terminate
C CALLP TERM(ECX :OPSTA )
So question is what kind of changes does this snip need to make it work
now that ECX is now a pointer to a long?
I thank you for all input in advance
Terry
D SCB DS
D ECXPtr *
D SID 9B 0
D COMMD 12
D OPSTA 9B 0
D INIT PR
D ECXPtr *
D OPSTA 9B 0
D CONNECT PR
D ECXPtr *
D SCB 220A
D XUSER 12A
D TERM PR
D ECXPtr *
D OPSTA 9B 0
D ECX S 9B 0 Based(ECXPTr)
FYI, you can't have a based data structure subfield
And for the procedure calls :
C Z-ADD 0 ECX
C CALLP INIT(ECXPtr :OPSTA )
... Do some error checking and connect if ok
C CALLP CONNECT(ECXPtr :SCB :
C XUSER :LGHID :
C XPASSW :LGHPAS :
C XSERV :LGHSRV)
... When done terminate
C CALLP TERM(ECXPtr :OPSTA )
HTH
--
Christian Gstalder
"Terry Schwarz" <terry....@comcast.net> a écrit dans le message de
news:Xns94C56D186CFF7te...@216.196.97.136...
I'm a little confused. The prototypes you've posted are already using
pointers. RPG by default passes parameters by reference, hence what
gets passed is the pointer to the variable you're passing.
For RPG to pass the value, you would have to have used the VALUE
keyword.
Here's a couple of examples:
void someproc1(int num);
void someproc2(int *ptr2num);
d SomeProc1 pr extproc('someproc1')
d num 10i 0 VALUE
d SomeProc1 pr extproc('someproc2')
d ptr2num 10i 0
Here's some more info:
http://www.opensource400.org/callc.html
Can you post the C prototypes?
Also you should try to get away from using the B data type. Current
versions of the RPG compiler support the I-Integer and U-Unsigned
integer types which would be more correct. Ranges for the B,I,U types
(using 4 bytes) are as follows:
B -999,999,999 (I think?) to 999,999,999
I -2,147,483,648 to 2,147,483,647
U 0 to 4,294,967,295
HTH,
Charles
In article <Xns94C56D186CFF7te...@216.196.97.136>,
terry....@comcast.net says...
If none of the answers you get are any help, then could you post the old
and new C structures and prototypes? It's not clear to me which ECX
you're talking about.
But assuming that the first parameter to say the INIT function has
changed from
long *
to
long **
then the RPG definition for INIT would look like this:
D INIT PR
D ECX 9B 0
D OPSTA 9B 0
D ECX S 10I 0 BASED(pECX)
D pECX S *
To call CONNECT:
CALLP INIT(pEXC : whatever);
Probably INIT sets the pointer, so that's all you need, but if INIT sets
the pointer, you need this instead:
D ECX S 10I 0
D pECX S * INZ(%ADDR(ECX))
Christian, assuming you've interpreted the question correctly, and ECX
in question is the subfield of the data structure, how ECX is defined
depends on whether INIT sets it up or whether the caller is supposed to
set it up.
If the INIT function sets it up, then your definitions are correct,
except for the assignment to ECX. You can't assign to ECX before ECXPtr
is pointing to some storage.
If the caller sets it up, then define it like this:
D ECX S 9B 0
D SCB DS
D ECXPtr * INZ(%addr(ECX))
D SID 9B 0
D COMMD 12
D OPSTA 9B 0
In C terms, this is equivalent to:
If INIT sets up the pointer:
SCB_t SCB;
INIT(&SCB);
If the caller sets up the pointer:
long ECX;
SCB_t scb;
scb.EXCPtr = &EXC;
INIT(&scb);
I believe the RPG side is not creating the pointer but just reading it
passing it back and forth as a context for a server type program,
I will check the various suggestions out and let you know outcome or post
more detail as requested.
Thank you again.
Terry
Many thanks for the precision.
Your posts are always a great help for the AS/400-iSeries community.
--
Christian Gstalder
"Barbara Morris" <bmo...@ca.ibm.com> a écrit dans le message de
news:4075B1B8...@ca.ibm.com...
I did Christian's suggestion with Barbara's correction (but not sure if I
got it quite right). Barbara's other posting wasn't complete enough for me
to understand so what I tried didn't compile.
Before posting the original newsgroup item, I tried Charles suggestion about
VALUE option and I had looked at the url he mentioned, but I think that non
RPG thing bit me again, but will look further at it.
Barbara, what I didn't understand about your correction was that you said
...
> If the INIT function sets it up, then your definitions are correct,
> except for the assignment to ECX. You can't assign to ECX before ECXPtr
> is pointing to some storage.
and go on to show coing for if caller sets it up, but not if INIT does it,
which I believe is the case
Below is the C prototypes from the .h files and a snip of C usage. In
reading them it seems I was also was mislead since I was told it was pointer
to a long and see void* in the typedef ...
typedef void* XXX_ID; /* context handle storage area */
typedef struct
{
XXX_ID eid; /* Context identifier */
long sid; /* Session identifier */
char command[12]; /* Most recent command executed */
long status; /* Status code */
long nbrcols; /* Column count */
long a_size; /* Tuple size (ALPHANUM) */
long b_size; /* Tuple size (BINARY) */
long count; /* Row count */
long msg_type; /* Message type code (0 = No message) */
char msg_org[8]; /* Message originator */
long msg_code; /* Message code */
char msg_text[136]; /* Text of message (null terminated) */
long msg_len; /* Length of message */
long msg_pending; /* Message pending indicator */
long msg_overflow; /* Message queue depth exceeded */
char user[8]; /* Reserved area for user */
char parm_count; /* 37323 number of prepare replacement fields */
char reserved; /* Reserved area for future use */
char xopen[6] ; /* xopen code associated with received message */
} XXX_scb, XXX_SCB; /* 17814 request for all caps */
int main(int argc, char *argv[])
{
XXX_ID eid; /* context id for this application. */
XXX_scb scb; /* An scb. */
long status; /* Status return codes from API */
initialize_xxx(&eid, &status);
if (check_stat(&scb, scb.command, status) != SUCCESS)
exit(1);
"Barbara Morris" <bmo...@ca.ibm.com> wrote in message
news:4075B1B8...@ca.ibm.com...
Hi and I'm back ... may be I'm not getting it because I'm not an RPG
person and just hacking at this.
I did Christian's suggestion with Barbara's correction (but not sure if
I got it quite right). Barbara's other posting wasn't complete enough
for me to understand so what I tried didn't compile.
Before posting the original newsgroup item, I tried Charles suggestion
about VALUE option and I had looked at the url he mentioned, but I think
that non RPG thing bit me again, but will look further at it.
Barbara, what I didn't understand about your correction was that you
said ...
> If the INIT function sets it up, then your definitions are correct,
> except for the assignment to ECX. You can't assign to ECX before
> ECXPtr is pointing to some storage.
and go on to show coing for if caller sets it up, but not if INIT does
it, which I believe is the case.
With sugestion as is we get past INIT case, but fails at CONNECT and it
appears API is not getting info as expected, but may be because INIT
didn't quite init correctly.
> > D OPSTA 9B 0
> > ...
> > D ECX S 9B 0 Based(ECXPTr)
> > ...
> > C Z-ADD 0 ECX
Is this C code working properly?
Terry Schwarz wrote:
> int main(int argc, char *argv[])
> {
> XXX_ID eid; /* context id for this application. */
> XXX_scb scb; /* An scb. */
> long status; /* Status return codes from API */
>
> initialize_xxx(&eid, &status);
> if (check_stat(&scb, scb.command, status) != SUCCESS)
> exit(1);
>
First you call initialize_xxx with the address of a stand-alone pointer to a
context id. That suggests that initialize_xxx uses the address of your pointer
to supply you with a context id. Next you call check_stat with a struct that
contains another, apparently not initialized, pointer to a context id. Shouldn't
that be the same pointer?
Anyway, your original prototype:
D INIT PR
D ECX 9B 0
D OPSTA 9B 0
should change to
D INIT PR
D ECX *
D OPSTA 10I 0
The first parameter, ECX, is now a pointer. Because parameters in RPG are passed
by reference (unless you specify VALUE), you are passing a pointer to a pointer,
like the C code does. The second parameter is an integer, so it should be coded
as an integer. The 'B'inary data type was the only way to handle integers before
RPG got proper integer support.
Another thing: Coded this way, the binder (= linker) will search for a function
with the name INIT (all uppercase). Assuming the actual name is
'initialize_xxx', you should code:
D INIT PR EXTPROC('initialize_xxx')
D ECX *
D OPSTA 10I 0
Joep Beckeringh
I just read my post and for a second I thought I'd left something out,
but now I remember that I didn't bother to post the "if INIT sets it up"
code, because I'd already quoted Christian's code which I thought was
correct. (And I still think his D specs were correct.)
If you're still having trouble after reading Joep's posting, could you
post the C prototypes? You posted the C type definitions and some
sample C code, but not the prototypes. They would look something like
this:
void initialize_xxx(XXX_id *, long *);
int check_stat(XXX_scb *, char *, long);
Seems that when we were using the long you could get away with the API parms
using the the long as the handle to the API even though it was part of the
control block, but not when it became a pointer. The original RPG shouldn't
have done it that way and it was leading us down a wrong alley.
Basically we declared a standalone field as "*", the ECX's as "*", cleaned
up the various PR and DS declarations to use lengths (vs.from/to positions)
for the vars and used the standalone var name (vs. ECX) as the handle in the
api and all it started working.
I thank everyone for there time and efforts.
Terry
"Barbara Morris" <bmo...@ca.ibm.com> wrote in message
news:407ACB88...@ca.ibm.com...