Search code examples
rpgle

How to convert an RPGLE function prototype to be called from a 'C' program?


I have an existing RPGLE function that I need to call from a C ILE program. It returns a data structure, and I don't seem to be able to work out what the C prototype, and hence call would look like? I have tried to extract the essence of the problem below.

dcl-s idT int(10) template;

dcl-s param1T char(25) template;                                                         
dcl-s param2T char(100) template;  

dcl-ds paramT qualified template; 
  param1 like(param1T);                                               
  param2 like(param2T);
end-ds;

dcl-pr fn1 likeds(paramT);                             
  id like(idT) const;                                                   
end-pr;  

I can see that the input parameter is a pointer to an int, so I have something like this ...

paramT fn1(int *id);

But the value being returned is not correct.

I am expecting the value of the calculate values to come back in the returned data structure (debugging the call shows that the correct values are being calculated).

EDIT: This is the 'C' side of the call (which almost certainly where the issue is) ..

typedef _Packed struct       
{                            
   char param1 ^25];        
   char param2 ^100];    
} paramt;    

paramt *FN1(const int *id);

paramt *value = FN1(&id);       

Solution

  • While RPGLE can receive parameters by value or by reference, it always returns a value. So you should define the function like this:

    paramt FN1(const int *id);
    

    and call it like this:

    paramt value = FN1(&id);
    

    There is a caveat to this. When returning something short, like a char(1), C likes to widen bytes to an integer, and you may need to add ExtProc(*cwiden) or ExtProc(*cnowiden) to your prototype. More information here.

    Also there is the issue of case. You properly handled this in your examples, but for completeness, RPG is case insensitive when it comes to identifiers. It converts everything to upper case unless told otherwise. This can be jarring for C developers who like to use lower case. You can add *dclcase to ExtProc to tell the compiler not to convert the procedure name to upper case, but instead use the case of the prototype. So:

    dcl-pr fn1    LikeDs(paramT) ExtProc(*dclcase);
      id          Like(idT) const;
    end-pr;
    

    Can be called like:

    paramt value = fn1(&id);
    

    More info about *dclcase here.

    Finally there is RTNPARM. This is a keyword that can be added to an RPGLE procedure to tell it to treat the return value as a parameter. This can be useful when returning large strings or structures to avoid putting a large value on the stack. This is a performance issue. While RPGLE knows how to deal with this, there is no way (via prototype) to tell a C program that the return value is in the first parameter. You just have to code your C prototypes to have a pointer to the return value in the first parameter location. More info about RTNPARM here. The multi-language example is CL, but you should be able to get the gist.