Midrange News for the IBM i Community


Posted by: Bob Cozzi
Rogue Programmer
Cozzi Productions, Inc.
Chicagoland
Using C C++ from RPG IV
has no ratings.
Published: 16 Jul 2015
Revised: 25 Jul 2015 - 3198 days ago
Last viewed on: 23 Apr 2024 (3430 views) 

Using IBM i? Need to create Excel, CSV, HTML, JSON, PDF, SPOOL reports? Learn more about the fastest and least expensive tool for the job: SQL iQuery.

Character Return Values from C Functions Published by: Bob Cozzi on 16 Jul 2015 view comments

Returning Fixed-Length Data from C to RPG IV

Being so use to RPG IV, when you need to return a 10-position character value, you code the return for your subprocedure as "10A" and you're done. When that subprocedure is actually a C function, things are little more tricky, but not once you know the secret.

In this blog entry, I am going to illustrate how to "pass" a fixed-length character field from C to RPG IV as a return value of a subprocedure/function call.

In RPG IV, all fields occupy a fixed space. Character and VARYING both occupy the their declared size, in bytes. VARYING occupying 2 or 4 additional bytes for the hidden length.

In C a similar design exists where whatever size a field occupies is fixed. However in C you can look at fields in myriad ways, based on how you reference that variable. When it comes to parameters you have to help the compiler know how long the return value is going to be.

Let's look at an example. I'm using the GetSysName function I created last time (press the Prev link above to visit that blog entry). GetSysName returns an 8-position character value. The RPG IV Prototype looks like this:

     D getSysName      PR             8A   extProc('getSysName') 

This prototype has no parameters but has an 8-position character return value. To use in RPG IV, it would be coded as follows:

     D SysName 8A
     /free
            sysName = getSysName();
     /end-free

We all know how to write a subprocedure in RPG IV that returns a character field, but what about C?

Normally a character value such as a string of characters is returned by declaring a point to that value, for example:

char* getSysName()

The issue is that RPG can't deal with this. Why? Because it actually is returning a point to a character, so our example the RPG IV prototype would have had to be modified as follows:

     D getSysName      PR              *   extProc('getSysName') 
     D SysName        s             8A
     D pSysName      s             *     Inz(%Addr(sysName))
     /free
             pSysName = getSysName();
     /end-free

The C code for this would be as follows:

char* getSysName()
{
   char sysName[9];
   /* Get the system name */
   strcpy(sysName,"IBMIRD");
   return sysName;
}

The problem is this will fail. C returns pointers to the data not the data itself when a character variable is specified. As RPG programmers we tend to avoid the use of pointers so this isn't exactly a great solution. So rather than modify the RPG to handle this let's look at how the C code can be adjust so it returns a fixed-length character variable that RPG expects.

Return Data Structures from C to RPG

The C Language supports structures similar to RPG. As it turns out if we return a structure as the return value from C, it is returned the same way RPG returns it.

typedef _Packed struct tagSysName8 {
    char sysname[8];
} SYSNAME8;

SYSNAME8 getSysName()
{
   SYSNAME8 sysName;
   /* Get the system name */
   strcpy(sysName,"IBMIRD");
   return sysName;
}

This returns it as expected by our RPG IV prototype and everything works.

Since there are so many different lengths possible, I've created a C macro to build the size structure correctly. It's called CHARFIELD and accepts one parameter.

#define CHARFIELD(len) \                                       
  typedef _Packed struct { char buffer[len]; } CHARFIELD##len  

For example:

CHARFIELD(8);
CHARFIELD(10);
CHARFIELD(20);

 

This defines structures (typedef's actually) named:

CHARFIELD8
CHARFIELD10
CHARFIELD20

If other sizes are needed just specify the length:

CHARFIELD(x);

And it creates CHARFIELDx for you. Then use that as the datatype of the return value.

Now, the following RPG IV code, works as expected:

     D getSysName      PR             8A   extProc('getSysName') 
     D SysName         S              8A
     /free
            sysName = getSysName();
     /end-free

But this obviously doesn't return the real System Name. To do that we need to create a real C function. Here's my attempt at it:

 

extern "C" CHARFIELD8 getSysName( void );
CHARFIELD8 getSysName(  )
{
      _MMTR_Template_T  mchInfo;
      short mAttr = _MMTR_APPN;
      int   len   = 20;
      CHARFIELD8 rtnSysName;

            // Note: System name uses first ~20 bytes.
      memset(&mchInfo,0x00,sizeof(mchInfo));

      mchInfo.Options.Template_Size = len;
      _MATMATR1( &mchInfo, &mAttr);

      len = min(sizeof(rtnSysName.buffer,mchInfo.Options.Data.APPN.Name_Len);

      memset(rtnSysName.buffer,' ',sizeof(rtnSysName.buffer));
      memcpy(rtnSysName.buffer,mchInfo.Options.Data.APPN.Sys_Name,len);
    return rtnSysName;
 }

Thanks to ILE and the now 21-year old RPG IV compiler, we can call C functions as if they were written in RPG. Of course for this example, we could have embedded the MI instructions right into the RPG code, but we would need to prototype the _MATMATR1 instruction first. If you have a copy of my Cozzi Tools (www.coztools.com) there's a prototype member named MIPROTOS in QCPYSRC that contains many MI prototypes.

What about *CWIDEN? Not needed for this particular prototype, but it could have been specified for those who prefer a more riged rule/standard. Simply prefix the 'getSysName' with *CWIDEN: as follows:

extproc(*CWIDEN: 'getSysName')

Works just as well.

 

Return to midrangenews.com home page.
Sort Ascend | Descend

COMMENTS