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: 13 Jul 2015
Revised: 13 Jul 2015 - 3210 days ago
Last viewed on: 26 Apr 2024 (3594 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.

C++ Functions Published by: Bob Cozzi on 13 Jul 2015 view comments

To continue where I left off last time; we need to complete the "centerText" function which has been written in C++ and exposed as a C language interface to RPG IV.

Last time there was a helper function called. That function is named trim(). But trim() actually uses ltrim() (left trim) and rtrim() (right trim) functions.

#include <stdlib.h>
#include <string>

using namespace std;


extern "C" void centerText( char* ioText, int textLen);          // Center text

int  trim(std::string& stg, const char* trimChars = " ");   // Trim both
int  ltrim(std::string& stg, const char* trimChars = " ");  // Trim left/leading
int  rtrim(std::string& stg, const char* trimChars = " ");  // Trim right/trailing

void centerText( char* myText, int textLen)
{
   std::string text;
   if (textLen <= 1)
      return;

   text.assign(myText,textLen);
   trim(text);
   text = std::string((textLen - text.length()) / 2, ' ') + text;
   memset(myText,' ', textLen);
   memcpy(myText,text.c_str(),text.length());
   return;
}

int   ltrim(std::string& stg, const char* trimChars)  // Trim right
{
     int found = 0;

     if (trimChars == NULL)
      found = stg.find_first_not_of(" ");
     else
      found = stg.find_first_not_of(trimChars);

     if (found == std::string::npos)  // Contains nothing except "trimChars"?
        stg.clear();                  // then Empty the string
     else if (found > 0)
        stg.erase(0,found);

     return stg.length();
}

int  rtrim(std::string& stg, const char* trimChars)  // Trim right
{
     int found = 0;

     if (trimChars == NULL)
      found = stg.find_last_not_of(" ");
     else
      found = stg.find_last_not_of(trimChars);

     if (found == std::string::npos)
        stg.clear();   // The string is all blanks, so clear it.
     else if (found+1 < stg.length())
        stg.erase(found+1);

     return stg.length();
}

int   trim(std::string& stg, const char* trimChars)  // Trim right
{
    ltrim(stg, trimChars);
    return rtrim(stg, trimChars);
}

I've named these 3 functions similar to the SQL functions of the same name. They accept a reference to a C++ string class along with optional trim characters (2nd parameter). If no 2nd parameter is specified they default to blanks (see the prototypes near the top of the source code for default/optional parameter syntax).

Using the string class's find_first_not_of() function (yes, that's its name) we can easily locate the first non-blank character in the original RPG variable (for the ltrim() function) and using find_last_not_of() we can locate the last non-blank position (for the rtrim() function). Once we locate these positions we isolate the data by removing the trailing and leading blanks. This gives use the ability to calculate the number of blanks needed in front of the data in order to center it.

The three new functions are:

  1. trim - trim leading and trailing blanks by calling ltrim and rtrim.
  2. ltrim - trim leading or "left side" blanks.
  3. rtrim - trim trailing or "right side" blanks.

What you'll find as you continue to use C and C++, is that you create a lot of "helper" functions, such as ltrim, rtrim, and trim. The overhead for such functions is nomincal, much less than standard RPG procedures so doing a function call in C/C++ doesn't add too much to your overhead. In fact, I wouldn't even think about it. RPG IV on the other hand, does a lot of parameter copying, conversion, and operation description stuff that makes RPG IV a very safe language. With C/C++ it is up to the programmer to do things right.

Note that we do NOT need to include the extern "C" keyword before our trim routines. Since only the centerText routine is called from RPG, it is the only one that needs the extern "C" keyword. This allows use to use C++ class objects as parameters and get more work done with less code.

While this is about RPG and C/C++, I will note that the IBM i Machine Interface or "MI" language exposes the triml() function to the C language. This function "trim length" scans, starting on the right-side of the variable for the last non-blank character. It returns the position of that character giving you the length of the data in the variable (hence "trim length"). Now, MI instructions are the best performers when it comes to doing things, so while the string::find_last_not_of() function is convienent, triml() would be a lot faster. So why didn't I use it. Original I had used it, however to make things consistent, since I had to trim off both trailing blanks (which is supported with the MI triml instruction) and leading blanks (which is not) I decided to use a consistent interface. Note that user-specified "characters to trim" are supported by the string class and the MI instruction.

Defaults on the Prototypes

C++ allows programmers to code default values for parameters. Note that all 3 of the trim helper functions have an optional 2nd parameter (trim characters) that accepts any number of characters. If it is not specified, it defaults to blanks. To assign a default value, simply specify the function's prototype, and include the equals sign after the parameter, followed by the default value. For example:

void trimLeft( string& stg, const char* trimChars = "  ");

The 2nd parameter, trimChars, may be specified by the programmer, but if it is not, then the compiler generates code to pass a blank 1-character string to the function. I LOVE THIS FEATURE of C++ and wish RPG IV had something similar. This is also a way to allow parameters to be called with 1, 2, 3, or more parameters, similar to specifying OPTION(*NOPASS) on RPG IV parameters. Not strictly the same, that is you can't map RPGi's *NOPASS to C++ default parameters. In fact RPG CAN NOT CALL C++ FUNCTIONS. This is a big disappointment.

Next time, return parameters, or how to return a fixed-length character variable from C/C++ to RPG IV.

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

COMMENTS