12
Tue, Nov
5 New Articles

Using C Functions in ILE RPG

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Impossible though it may seem, you can bind C functions to RPG IV programs. Since so much software has been written in C, you may very well find that great solutions to the problems you face have already been written in C for other systems and are just waiting for you to put them to work on your AS/400. This article uses C’s character-to- number conversion functions to show you how to bind C functions to RPG.

When I was asked to write an article that would adequately demonstrate the use of C functions in ILE RPG applications, I was hard-pressed to think of practical uses. After all, when you think about it, there are only two reasons to use C functions in an RPG application: Either the C function is superior to the RPG-equivalent operation, or no RPG- equivalent operation exists.

For example, say you want to read a customer file by the customer number key. In RPG, you would probably accomplish that with a CHAIN operation. To do the same thing in C (after having previously called the _Ropen()function to open the keyed file and receiving a pointer or handle to the file), you would need to call the Read a Record by Key (_Rreadk()) function specifying the file handle, address of the record buffer to receive data read, and the address of the key used to access the file. Additionally, specifications for file name, open mode, access options, and so on, would need to be passed as null-terminated strings, adding unnecessary complexity to a language that has a far better solution to the problem. What self-respecting RPG programmer would choose the C function over the efficiency and elegance of the CHAIN operation?

Yet many functions have been developed for both the standard C library and “offthe-shelf” libraries that were developed to handle specific problems in particular industries and to provide a higher level of access to more primitive functions in order to improve programmer productivity and code reuse. And while C is being phased out (very slowly) by its object-oriented incarnation, C++, it is still a thriving language (like RPG), enjoying the full support of many language vendors. This accessibility and availability of language extensions should be factored into your thinking when you make decisions about design implementations. In some cases, the availability of C functions to handle application features may even change the design of an application (yes, even on the AS/400). An example of this continued support can be found in recent additions made to the Call Level

Interface (CLI) for SQL (ODBC on the AS/400). All the new functions implemented to support the native JDBC driver (and implemented in the CLI) for the AS/400 have been written in C. This is not a coincidence. When performance and low-level access to system functions are required, IBM systems development teams have relied on the indisputable superiority of C. In addition, when binding to Java functions using the Java Native Interface (JNI), the steps described in this article can be equally applied to that process (see Don Denoncourt’s sidebar, “Using C Functions with Java” on this subject). Yet, notwithstanding RPG’s overwhelming support for file I/O, where can you tap into the vast reservoir of language features that C programmers enjoy each day?

What About Concatenation or Substringing Operations?

To use the Concatenate Strings (strcat()) and String Copy (strcpy()) functions, you would have to append a null-terminator character (hex 00) to the end of each text parameter used in these functions because C expects null-terminated strings to be passed. With RPG IV, however, you can accomplish the same thing—elegantly and more intuitively—with the

EVAL statement, the + operator, and the %SUBST built-in function. We have a bounty of operations in RPG to handle file access and text manipulation, as well as a number of other things that C doesn’t have. What, then, is a practical application of C functions that does not already exist in RPG?

Absence Makes the Heart Grow Fonder

Given an acceptable RPG solution, using C functions in an RPG application is not my first choice. However, I must concede to the existence of a set of standard functions that deal more extensively with text conversions than RPG. This is the area where C stands to fill the void left by an incomplete set of ILE RPG built-in functions. For releases V3R7 and up, built-in functions were delivered that enable the conversion of zoned and packed numeric fields to float (%FLOAT) and integer (%INT) data types. Yet, conspicuously absent is a function to convert text to float or integer data types.

Why would you want to do this? Well, suppose you had a feature in your application to allow the user to enter alphabetic and numeric characters in a text field for comparative operations to be executed by another process. You would need to convert the textual expression of, say, 100.00 to a numeric-compatible data type that could then be compared to a numeric field value. As I mentioned, there are operations to convert decimal and nondecimal, zoned and packed, numeric field values to float and integer data types. So I can, for example, take a value from a field called AMT and convert it to a float data type with %FLOAT(AMT). However, now I need to take the textual expression of 100.00 and convert it to a float as well (to do a compatible comparison operation), but how? ILE RPG currently has no equivalent for the C functions atof() and atoi() that do just that.

A Rose by Any Other Name...

What, exactly, are the atof() and atoi() functions anyway? IBM describes the atof() as the Convert Character String to Float function, and the atoi() as the Convert Character String to Integer function. Given that the history of C parallels that of UNIX (both were developed at Bell Laboratories), and UNIX uses an ASCII encoding scheme for data storage and recognition, the a in atof() means ASCII, which is used for typical text or byte- stream files, such as those used in UNIX. So, the atof() (or ASCII to Float) and the atoi() (or ASCII to Integer) functions are used by C programmers to convert textual or string representations to float and integer data types, respectively, for comparison operations.

With ILE RPG, you, too, can use these operations to convert your character or text fields to a compatible data type for comparative operations. Before I get to the solution of my immediate problem, I will need to know how to map C data types to equivalent ILE RPG data types and read a C function prototype to convert it to an ILE RPG prototype.

Figure 1 presents a table showing how each C data type maps to equivalent ILE RPG data types.

A Case of Some Importance

In RPG IV, whether or not a subroutine or field is keyed as uppercase or lowercase is of no importance since both are converted to uppercase during compilation. Yet, in C, which is case sensitive, whether you use uppercase or lowercase can make all the difference in the world. If you attempt to bind to a C function identified as atof (all lowercase) in an EVAL statement, atof will be converted to ATOF (all uppercase) during compilation, and the subsequent C function atof will not be found. To avoid this problem, in the prototype for the atof function, you can define the function by identifying it in the ExtProc keyword as ExtProc(‘atof’). Such a declaration preserves the case of letters enclosed by the quotes. Then, you will be able to bind to any C function no matter what the case or special characters might be.

Batteries Not Included!

Let’s get a bit of terminology out of the way. When discussing C functions, the term include (#include or header) files arise. Include files are identical to /COPY file members in RPG. For a standard set of C library functions on the AS/400, most of the #include headers can be found in library QCLE, source physical file H, member STDLIB.

Compile-time macros are defined using the #define compiler directive. These macros are replaced by their corresponding values when encountered in source statements during compilation.

In order to properly bind with and utilize C functions, you must first understand what the functions require in the way of arguments and return values. All of this information is disclosed in the function prototype. Standard C function #include header files come with the ILE C/400 licensed program product. If you do not currently license this product, you must request it from IBM’s System Development Division for the ILE C/400. Be prepared to furnish a waiver of some sort, verifying that you do not expect any support (and rest assured that support will not be forthcoming from IBM) should you encounter problems using these functions. You may be told that such includes cannot be distributed except to licensed program product holders. Don’t dismay; I am told that these include headers are being considered for distribution in the Openness Includes library, QSYSINC.

The C #includes, defined in the QCLE/H file, contain all of the C function prototypes for the standard C functions. These includes are required to determine the number of arguments passed (and their data types) to C functions, values returned from these functions, and the value given to any compile-time macros passed to these functions. Only when you know this information can you successfully bind to these functions. Figure 2 illustrates the prototypes for the atof() and atoi() functions that I will be using in my example.

The atof() function prototype expects a pointer to a character string to be passed to it, as indicated by the phrase const char * enclosed in parentheses. It also expects to return a double-precision floating point value upon successful execution. Figure 3 presents a table that shows the format that you can expect each C function to adopt and gives you an idea of how to convert it to an ILE RPG subprocedure prototype.

Using the format shown in the table for Figure 3 and the data mapping table presented in Figure 1, you should be able to successfully prototype each function to the ILE RPG equivalent, as shown in Figure 4. You will note from Figure 4 that the pointer to the character-string representation of the number or amount is passed by value and not by reference. Most of the standard library functions will require arguments to be passed by value since they will operate upon a copy of the data rather than upon the actual data. If you forget and omit the Value keyword from arguments passed, the compiler will issue error RNF7542 (“Parameter cannot be passed by reference when it can be changed during the call”). If you receive this error, add the Value keyword to your prototype definition for the function, and recompile—it should properly compile.

Completion of the Task

To put our C functions to the task, I have developed a trivial application that allows the user to pass one parameter as a text string representing some number (no decimals) or some amount (decimals). The user can pass a negative number or amount by prefixing the value with a minus sign (such as -100). Since this is a trivial application meant only to demonstrate the use of C functions in an ILE RPG program, I have not added any bells or whistles. The application simply displays the converted string passed as a float or integer value with the DSPLY operation code, and compares the value passed to 100.00 or 100. It then displays another message about the value’s relationship to 100, as shown in Figure 5.

Case 1 of Figure 5 shows the results when an amount (-980.50) is passed in a call to String to Numeric Value (StrToNV), my program. Note that since the value passed has decimals, the string is converted to a float for purposes of the comparison. The relationship shows that -980.50 is less than 100.00. Case 2 of Figure 5 shows the results when a number is passed as a text argument to StrToNv. Note that 101 was passed, and the resulting relationship test shows that 101 is greater than 100. Thus, the string was converted to an integer for purposes of the test.

Figure 6 illustrates the program source to convert a string of text representing a numeric value to either a float (if it has decimals) or an integer (if it has no decimals). At Label A of Figure 6, the instructions for compiling the program are presented. Note that, in order to successfully compile StrToNV, you must specify the binding directory QC2LE. This binding directory includes the service program that contains the two C functions that I am binding to my application. That service program is Q2UTIL1. Using the DSPSRVPGM SRVPGM(QC2UTIL1) DETAIL(*PROCEXP) command, you can review all of the C functions available in that program. The table presented in Figure 7 shows four service programs in the QC2LE binding directory that make up the bulk of all the C functions you will ever need (unless you are developing a TCP/IP socket program—the subject of an upcoming article).

At Label B of Figure 6, our /COPY statements (or includes, using C parlance) for the function prototypes for atof() and atoi() are defined. Label C denotes the beginning of some fields defined to support the features of StrToNv; in particular, we have defined a float (designated by the f type) and an integer (designated by the i type). The float field TPakFloat will be used to hold the converted packed amount (MyPackedAmt) that was defined with an initial amount of 100.00. MyPackedAmt is included here only to demonstrate the use of the built-in function %FLOAT, as shown at Label E. TPakFloat has been defined as a double (8f), as well as StrInFlt, that will be used to hold the result of the character string conversion function atof() at Label F. TZonInt has been defined as a 4-byte integer to hold the conversion of MyZonedNbr defined with an initial value of 100 at Label
C. The conversion of MyZonedNbr to TZonInt is demonstrated at Label H, while Label I shows the passed string argument StringToCvt field being converted to an integer (no decimals) with our bound C function atoi() for comparison with TZonInt immediately below that. Finally, at Label J, using the EVAL to concatenate the string argument with the selected relationship evaluation, I can display the results with the DSPLY operation code as a last step before ending the application.

More There Than Meets the Eye

When I first started to do the research to write this article, I must say that I was not convinced that there was that much in the C function service programs worth using in an ILE RPG application (unless you had a scientific or statistical program you needed to develop). However, after doing a little legwork and a little generalizing, I found that I have an ongoing need to use these functions in current applications. For example, I have used just these two functions (atof() and atoi()) in an application that enables users to state, in text, their predicate conditions for applying business rules (rules-based applications) for data selection. Additionally, I have used C socket functions for developing ILE RPG TCP/IP socket programs to communicate with applications running on NT servers (emailing invoices to customers). I think that, after reading this article and mulling over

your own projects, you will come to the same conclusion. While RPG can be used to develop subprocedures that are capable of doing the same things as our atof() and atoi() functions, why reinvent the wheel?

For another practical application using C functions where equivalent RPG functions are nonexistent, see “TCP/IP Socket Programming in RPG? Cool!” in a future issue of MC.

References

Essential JNI: Java Native Interface. Gordon, Rob. Upper Saddle River, New Jersey: Prentice Hall, Inc., 1998.

ILE C for AS/400: Programmer’s Reference V4R2 (SC09-2514-00, CD-ROM QB3AG100)

ILE C/400 Programmer’s Reference V3R7 (SC09-2072-01, CD-ROM QBJAQY01)

ILE RPG for AS/400 Reference V4R2 (SC09-2508-00, CD-ROM QB3AGZ00) ILE RPG/400 Reference V4R1 (SC09-2077-01, CD-ROM QBJAQE01)

Using C Functions with Java

I have been using the ILE/C compiler since V2R3. The C programming language is the easiest way to take advantage of the full breadth of OS/400’s system APIs. As you might expect, this is also the case in many other operating systems; after all, C has historically been the language of systems programmers.

For this reason, the Java programming language has a feature, the Java Native Interface (JNI), that allows you to easily integrate C (and C++) code with your Java applications. A Java class has a list of functions that describe the behaviors of the entity that the Java class represents. For those functions that you would like to implement with C, you simply add Java’s reserved word native as a modifier preceding the function declaration:

public class JNIExample { private int value; native void callCFunction(); int getValue() { return value;

}

static { System.loadLibrary(“C_CODE.SRVPGM”); }

}

The functions that do not have the native modifier provide Java code implementations between the enclosing curly braces of the Java function. The Java function with the native modifier has no code implementation; you’re going to be coding that in C, and then you’ll compile that C code into an ILE module. That ILE module is then bound into a service program. The service program is then associated with the Java class with a call to Java’s loadLibrary function. A good place to insert that loadLibrary call is in your Java class’s static pseudo-function. (Java guarantees that the pseudo-function is called the first time that class is instantiated.)

Note that, on an AS/400, the loadLibrary function must be passed a string that contains the dot SRVPGM qualifier. On a Microsoft platform, the loadLibrary qualifier would be dot DLL for a Dynamic Link Library. Your Java JNI class is compiled as usual with Java’s JAVAC command. What is different, however, with a Java class that uses JNI is that you then run Java’s JAVAH command, specifying your Java class as a parameter. The JAVAH utility then generates a C header file in a language native to your operating system. That file contains the C prototypes for the Java functions that you said you would use. The C function prototypes contain not only any parameters that correspond to the Java class functions, but also a C pointer (JNIEnv*) to the JNI environment and a special C structure called jobject. Through that pointer and the jobject structure, your C function can access the fields of the Java class as well as the other functions of that Java class.

In effect, the C function is just like its sister Java functions in that it has access to all of the fields and functions of its class. Realize, however, that the use of the JNIEnv pointer and the jobject structure does get fairly complex.

If you would like to know more about JNI, I suggest reading Essential JNI: Java Native Interface by Rob Gordon.

Soon, IBM will fully support the use of RPG modules with JNI, but until then, C is an excellent alternative.

—Don Denoncourt

C Data Type ILE RPG Comments

Data Type

int 10I 0 A signed 4-byte binary long [int] integer with 32-bit capacity

short [int] 5I 0 A signed 2-byte binary integer with 16-bit capacity

unsigned [int] 10U 0 An unsigned 4-byte binary unsigned long[int] integer with 32-bit capacity

unsigned short [int] 5U 0 An unsigned 2-byte binary integer with 16-bit capacity

char A (or blank) A single-byte character in C unless followed by an *, as in char *, in which case it is a pointer to a null-terminated character string

void * * A pointer to an unidentified data type

data type * * A pointer cast to point to a particular data type, unless void is used (In RPG, a pointer can point to any data type; no compiler checking prevents runtime errors.)

float 4F (no A float data type can be a equivalent float or a double; double and prior to long double are the same for V3R7) ILE RPG

double 8F (no equivalent prior to V3R7)

long [double] 8F (no equivalent prior to V3R7)

Figure 1: C-to-ILE RPG data type mapping

double atof( const char * );
int atoi( const char * );

Figure 2: C function prototypes for atof() and atoi()

Return Value Function Name Parameters or Arguments

Passed

double atof ( const char * )

int atoi ( const char * )

Figure 3: C function parts table for atof() and atoi()

*===================================================================

* Place in a source file called SYSINC with the member name of ATOFH
* and include with /Copy statement:
* Atof - Character String to Float Conversion
*

* Usage for Atof():
* EVAL StringAmt = Atof(%ADDR(StringAmtIn))
*===================================================================

** Compiler Include Directives
*/If Not Defined( AtofHCopied )
/Define AtofHCopied
/Else
/Eof
/EndIf

D Atof PR 8f ExtProc('atof')

D PtrToString * Value

**===================================================================

* Place in a source file called SYSINC with the member name of ATOIH
* and include with /Copy statement:
* Atoi - Character String to Float Conversion
*

* Usage for Atoi():
* EVAL StringNbr = Atoi(%ADDR(StringNbrIn))
*===================================================================

** Compiler Include Directives
*/If Not Defined( AtoiHCopied )
/Define AtoiHCopied
/Else
/Eof
/EndIf

D Atoi PR 10i 0 ExtProc('atoi')

D PtrToString * Value

*-

Figure 4: ILE RPG atof() and atoi() function prototypes

Case 1: Call strtonv parm('-980.50')

Results:
DSPLY String As Amount: -9.804999999999999E+002
*N
DSPLY -980.50 is less than 100.00

Case 2: Call strtonv parm('101')
Results:
DSPLY String As Number: 101
*N
DSPLY 101 is greater than 100

Figure 5: Sample of StrToNV run results

*=================================================================

* To compile:
*

* CRTBNDRPG PGM(XXX/STRTONV) SRCFILE(XXX/QRPGLESRC)
(A) * DFTACTGRP(*NO) ACTGRP(QILE) BNDDIR(QC2LE)
*

* -or-
*

* Take option 14 from PDM source member and prompt with F4
* to change the Binding directory parameter and the Default
* Activation Group parameter.

*

*=================================================================

*****************************************************************

* Function prototype includes.
*****************************************************************

(B) /COPY SYSINC,AtofH
/COPY SYSINC,AtoiH
*****************************************************************

* STANDALONE - Miscellaneous Standalone fields.
*****************************************************************

D FndDecAt S 5i 0 INZ(0)
(C) D TPakFloat S 8f INZ
D StrInFlt S 8f INZ
D TZonInt S 10i 0 INZ
D StrInInt S 10i 0 INZ
D MyPackedAmt S 13p 2 INZ(100.00)
D MyZonedNbr S 13s 0 INZ(100)
D StringToCvt S 20a
D CmpPhrase S 25a
D Message S 52a

*****************************************************************

* CONSTANTS
*****************************************************************

D StringAsAmount C Const('String As Amount:')

D StringAsNumber C Const('String As Number:')
C**********************************************************************
C *ENTRY PLIST
C PARM StringToCvt

(D) C EVAL FndDecAt = %SCAN('.':StringToCvt)

C SELECT

*C WHEN FndDecAt <>*ZERO
*(E) C EVAL TPakFloat = %FLOAT(MyPackedAmt)
(F) C EVAL StrInFlt = Atof(%ADDR(StringToCvt))

C StringAsAmountDSPLY StrInFlt *

C SELECT
(G) C WHEN StrInFlt = TPakFloat
C EVAL CmpPhrase = 'is equal to 100.00'
C WHEN StrInFlt > TPakFloat
C EVAL CmpPhrase = 'is greater than 100.00'
C OTHER
C EVAL CmpPhrase = 'is less than 100.00'
C ENDSL
*C OTHER

*(H) C EVAL TZonInt = %INT(MyZonedNbr)
(I) C EVAL StrInInt = Atoi(%ADDR(StringToCvt))
C StringAsNumberDSPLY StrInInt
*

C SELECT
C WHEN StrInInt = TZonInt
C EVAL CmpPhrase = 'is equal to 100'
C WHEN StrInInt > TZonInt
C EVAL CmpPhrase = 'is greater than 100'
C OTHER
C EVAL CmpPhrase = 'is less than 100'
C ENDSL
*

C ENDSL *

(J) C EVAL Message = %TRIM(StringToCvt) + ' ' +
C CmpPhrase
C Message DSPLY

C EVAL *INLR = *ON
C RETURN
C*********************************************************************

Figure 6: Source for StrToNV

Service Program General Purpose

QC2UTIL1 Math and memory APIs, where you will find the atof() and atoi() functions

QC2UTIL2 Bulk of standard C library of functions, where you will find the strcat() and strcpy() functions

QC2IO Record input/output functions, where you will find the _Rreadk() function

QC2SYS Consists of the system() function (Using this function would be similar to calling QCMDEXC with a command string.)

Figure 7: Bulk of standard C library and math functions

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: