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.
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:
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.
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.