Porting C Projects to IBM i

This document provides information on things you need to be aware when compiling C code which main target platform is not IBM i.

C Headers

The C Headers for most standard functions can be found at QSYSINC/H.

Compiler Directive

Probably some things will be configured and/or defined different on the IBM i platform as on other platforms. To differentiate between the OS types compile directives can be used. The IBM i platform automatically defines __OS400__ which can be queried like this:

#if __OS400__
  // IBM i platform
#else
  // other platforms
#endif

Endian Type

Some C code takes advantage of the endian type of the system and because of that it needs to know on what type of system it runs on when it comes to byte order. There are little endian and big endian types.

IBM i is a big endian type system.

See Wikipedia article about Endianness.

Include Files

Default include files (header files or copy books in RPG) are provided in the IFS at

  /QIBM/include

Data Types

There is an excellent paper about converting C prototypes and data types to its RPG equivalent from Barbara Morris, IBM:

Converting from C prototypes to RPG prototypes

Boolean

C does not come with many types like other languages and there is no type bool or boolean. But as there is the need for a boolean type in many libraries they defined true (equivalent to *ON in RPG) and false (equivalent to *OFF in RPG) in their code.

  #define true 1
  #define false 0

IBM i provides these in their header file stdbool.h so you can include it like this

  #include <stdbool.h>

size_t

size_t is a type definition and resolved to an unsigned integer of 4-bytes on the IBM i platform. Thus you can declare it as follows

  dcl-s length uns(10);

Variable Number of Parameters

C supports variable number of parameters for functions. This feature can be used by calling the functions va_arg, va_copy, va_start and va_end. IBM only recently has completed this set of functions as va_copy was not available on any release prior to OS release version 7.2.

See IBM Knowledge Center.

Structures and Pointers

The system will sometimes use more space for a structure than the space it is declared to use because the system will try to align it to a boundary 4, 8 or 16 bytes.

If a pointer is part of the structure and the P128 Data Model is used then a pointer is 16 byte large and needs to be aligned on a 16 byte boundary.

We are taking a string structure as an example:

struct string {
  const char * value;
  size_t length;
};

size_t resolves to a 4 byte integer type and a pointer (with the P128 data model) occupies 16 bytes of memory. This results in 20 bytes. But if we use the sizeof C function we see that 32 bytes are occupied by the structure. So in RPG we need to pad it with another 12 bytes.

dcl-ds string qualified template;
  value pointer;
  length uns(10);
  dummy1 char(12); // to fill up the 16 byte to the next boundary
end-ds;

In RPG the built-in function %size can be used to see how many bytes a data structure occupies.

These cases are not always covered with ALIGN keyword for a data structure. You might need to add some padding (by adding an additional dummy variable to fill up the space to the next boundary).
From the ILE C Language Reference:

To allow proper alignment of components, holes or padding may appear between any consecutive members in the structure layout.

Bitwise Shift Operator

« is the bitwise shift left operator which is often used to define constants like

  #define MG_F_SEND_AND_CLOSE (1 << 10)

In the example the symbol MG_F_SEND_AND_CLOSE has the value 1024. The following constant could be defined

  dcl-c MG_FLAG_SEND_AND_CLOSE 1024;

These constants are mostly used to be stacked into a single integer and the queried with bit operators like this

  flags |= MG_F_SEND_AND_CLOSE;
  
  ...
  
  if (flags & MG_F_SEND_AND_CLOSE) {
      ...
  }

The equivalent in RPG would be

  flags = %bitor(flags : MG_F_SEND_AND_CLOSE);
  
  ...
  
  if (%bitand(flags : MG_F_SEND_AND_CLOSE) = MG_F_SEND_AND_CLOSE);
      ...
  endif;

For more information on bitwise operators see Bitwise Operation on Wikipedia.

Pointer in Integers

In C you can do very tricky things but most of these things are not really compatible with most platform. Different platforms have f. e. different sizes for certain data types.

One of those tricks is to store a pointer in an integer. This will work on 32-bit systems as a pointer is 4 byte and an integer also occupies 4 bytes.

But on IBM i a pointer is 16 bytes (P128 data model) and an integer is just 4 bytes. This just won't work.

uname / utsname

In the Unix world there is the function uname which returns a structure utsname which has at least the following members:

  char  sysname[]  name of this implementation of the operating system
  char  nodename[] name of this node within an implementation-dependent communications network
  char  release[]  current release level of this implementation
  char  version[]  current version level of this release
  char  machine[]  name of the hardware type on which the system is running

IBM i doesn't support this by itself.

See https://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html .

Numbers declared as Hex

C devs like to declare their numbers in hex instead of just writing the numbers.

  #define UNQLITE_OPEN_CREATE           0x00000004

can be declared in RPG as

  dcl-c UNQLITE_OPEN_CREATE 4;

And

  #define UNQLITE_SYNC_FULL          0x00003

can be declared in RPG as

  dcl-c UNQLITE_SYNC_FULL 3;

Contributors

  • Mihael Schmidt
, , ,