Normally it is quite easy to convert from a C prototype to an RPG prototype. A C prototype has the following form:
return_type name ( parameters );
For example
double fn (int p1, int p2); /* function "fn" with 2 4-byte integer */ /* parameters, returning 8-byte float */
The mappings for the following list of C parameter types is straightforward:
C | RPG ------------------------+----------------------------------- int x | D x 10I 0 VALUE long x | D x 10I 0 VALUE long long x | D x 20I 0 VALUE unsigned int x | D x 10U 0 VALUE unsigned long x | D x 10U 0 VALUE unsigned long long x | D x 20U 0 VALUE double x | D x 8F VALUE | short *x | D x 5I 0 int *x | D x 10I 0 long *x | D x 10I 0 long long *x | D x 20I 0 unsigned short *x | D x 5U 0 unsigned int *x | D x 10U 0 unsigned long *x | D x 10U 0 unsigned long long *x | D x 20U 0 float *x | D x 4F double *x | D x 8FNote: There are some types missing from the top section of this list of easy-to-convert types. For example, char and short. These seem like they should be straightforward, but they are not.
Nearly as straightforward are the following parameter types.
C | RPG ----------------------+------------------------------------------ struct SSS s | D s VALUE LIKE(SSS) struct SSS *s | D s LIKE(SSS) TTT t | D t VALUE LIKE(TTT) TTT *t | D t LIKE(TTT) char *s | D s * VALUE OPTIONS(*STRING) |OR D s nA void *p | D p * VALUE |OR D s LIKE(SSS) wchar_t a | D a 1C VALUE wchar_t a |OR D a 1G VALUE wchar_t *a | D a nC wchar_t *a |OR D a nG ("nA", "nC", "nG" means any length, for example 10A, 25C, 100G etc.)
For any parameter that is passed by reference (with * between the type and the name in the C prototype), it is possible that the parameter is supposed to be an array. In C, type*x can mean "a pointer to the type" or "an array of the type". Usually the documentation for the function will make it clear. Array parameters can also be coded as type name[] (with no numbers between the []). If you do have an array parameter, you must code it in RPG as the type of the data, with the DIM keyword, and possibly with the CONST keyword, if the parameter is input-only. You must use the documentation to determine the dimension of the array.
where the RPG version of SSS is a data structure, and the RPG version of TTT is a field or data structure that matches the C version (a data structure or a type like 10i 0, etc.).
Note: Normally, you must code the ALIGN keyword on your data structure to match the C structure. Omit the ALIGN keyword if the _Packed keyword is coded on the C structure. For more details, see C named types (typedefs) and structures.
Return values are similar, with the added point that if a C function has a return value of "void", you just leave out the return type on the RPG prototype.
Here are a couple of examples of "easy" transformations. Try them yourself before reading the answers.
1. double sin ( double ); 2. struct s { int i; char c; }; void fn ( float *a, struct s b, struct s *c, unsigned short *d);
When you see "char *" in a C prototype, this could mean a few different things:
Problems with "C widening" can occur for these C parameter types, when passed by value:
Originally, C did not use prototypes; the C compiler would guess what type of parameter was being passed. This was fairly easy since C had only three basic data types, integer, float and pointer. Although it supported various lengths for these types, it only supported three parameter types for parameters passed by value. When the passed parameter had a shorter length from the supported parameter length, the parameter actually passed to the function was "widened" to be the length of the supported parameter type.
Here are the supported parameter types:
This implies single-byte character may be defined in RPG as 3U 0, not as 1A, when passing parameters to and from C by value, and handling procedure return values. But while defining the parameter or return value as 3U 0 may help, it will not completely solve the problem.
If, say, a short integer is passed as a parameter, it is widened to a long integer for passing; the called function receives the long integer and converts it to the type actually coded for the parameter.
Confused? Let's look at an example:
float f; x = fn (1, f, &x); (&x means the same as %addr(x) means in RPG)
The old C compilers didn't know anything about "fn". But they knew what to pass as a parameter here:
Even though C normally uses prototypes now, all C compilers still widen parameters by default, and when a C function is called, the compiler assumes that the parameters were widened.
Often, this doesn't seem to affect the RPG programmer at all. This is because problems due to widening are often hidden by fact that the system optimizes calls by using registers where possible. When a parameter is passed in a register, the fact that it was passed as 5i or 10i does not matter.
Problems with widening will typically show up when calls are made in complex expressions or when there are many parameters.
But even though problems don't often occur, it can be annoying and puzzling when they do crop up. Getting the prototype right in the first place will prevent the problem from ever happening.
Consider this C prototype:
void fn(char c);
The obvious (but incorrect) RPG version is
D fn PR 8F EXTPROC('fn') D c 1A VALUE
When the RPG program calls the C function, the compiler will place the parameter in the first byte of the parameter area.
C callp fn('A') +---+ Parameter area | A | +---+
The C compiler will assume that the parameter has been widened, so it will expect a 4-byte integer, which it will convert to a 1-byte unsigned integer.
+---+---+---+---+ Parameter area | A | x | y | z | +---+---+---+---+The 'A' will be ignored; the value that the function 'fn' will use is whatever happens to be in the 4th byte ('z' in this case).
Note: If the C function specified #pragma argument(nowiden) for the function, you should use EXTPROC(*CNOWIDEN). Even though widening is not in effect, coding *CNOWIDEN is still necessary for RPG to handle 1C and 1A correctly, when passed by value or returned.
You must code your parameter the way that C expects it to be passed.
D fn PR 8F EXTPROC('fn') D c 10U 0 VALUE
Then you must convert your character value to an unsigned integer. Here's a procedure that does this:
D cChar PR 10U 0 D A 1A VALUE P cChar B EXPORT D cChar PI 10U 0 D A 1A VALUE D DS D uns 10U 0 INZ(0) D chr 1A OVERLAY(uns:4) C eval chr = A C return uns P cChar E
You would call 'fn' this way:
C callp fn(cChar('A'))
It can, if you are using V5R1 or later, and you remember to code EXTPROC(*CWIDEN:'name').
You may have noticed that I said above that C widened parameter "by default". The C programmer can indicate that he doesn't want the parameters to be widened by specifying
#pragma nowiden(fn)
If this has been coded, the C compiler will behave like the RPG compiler regarding parameters passed by value. The RPG compiler cannot make any assumptions about the widening of parameters.
When you see "..." (three dots, called "ellipsis") in a C prototype, it means that there are optional parameters. The number and types of the parameters are normally determined by other parameters. For example, the "printf" function has the following prototype:
int printf(const char *, ...);
The first parameter is a null-terminated string indicating the value to be printed, with replacement variables indicated by %. For example,
printf("%s has %d contracts.", name, number);
says to substitute %s by the first parameter (name) and %d by the second parameter (number). Determining how to pass these parameters, and how to find out what the ellipsis means is beyond the scope of this article. For all functions that have ellipsis in the prototype, you must read the documentation for the function to find out what the optional parameters are.
Sometimes the matching RPG prototype will simply have to code the optional parameters the usual way, adding OPTIONS(*NOPASS). Sometimes, several different RPG prototypes will be needed for every possible call to the function.
Note: If a C prototype has "...", you MUST code OPTIONS(*NOPASS) for at least one parameter, for the other parameters to be passed correctly.
For any parameter that does not have the VALUE keyword coded, you have the possibility of coding the CONST keyword. Code this keyword when you know the parameter cannot be changed by the function. This is sometimes indicated by the documentation ("input parameter"), and sometimes the C prototype has "const" before the type.
int fn (const int *i1, int *i2, int i3);The RPG equivalent:
D fn PR 10I 0 EXTPROC('fn') D i1 10I 0 CONST D i2 10I 0 D i3 10I 0 VALUE
Conversion from a C named type to an RPG type is more straightforward than parameter declarations.
To find the actual definition for a named type XXX, search for code in one of the following forms.
The matching RPG definition for the type is coded with each example.
typedefXXX;
For example
typedef int XXX; D XXX S 10I 0
This could also be done in RPG as follows:
D int S 10i 0 D XXX S LIKE(int)
typedef*XXX;
For example
typedef float *XXX; D XXX S * typedef double* XXX; D XXX S * typedef SOMETYPE * XXX; D XXX S *
Note that the position of the * doesn't matter in the C definition.
Also note that since RPG does not have typed pointers, all the RPG definitions are the same.
typedef struct { type1 name1; type2 name2; ... } XXX;
For example
typedef struct xxx D XXX DS ALIGN { D i 10i 0 int i; D d 8f double d; D name 10a char name[10]; D c 1a char c; } XXX; typedef _Packed struct xxx D XXX DS { D i 10i 0 int i; D d 8f double d; D name 10a char name[10]; D c 1a char c; } XXX;
Note that the second structure has the keyword _Packed. This means that the equivalent RPG structure does NOT have the keyword ALIGNED.
Warning: Even if you code the ALIGN keyword, your RPG data structure might be a bit smaller than the C version. The C compiler pads out a struct so that its length is a multiple of its alignment. So if the struct contains a pointer, the length will be a multiple of 16. RPG does not do this, even with ALIGN, so the RPG data structure might be up to 15 bytes smaller than the C structure. The regex_t struct in qsysinc/h(regex) is an example of this; the C struct is 656 bytes long, but the natural RPG version is only 644 bytes long. The RPG version of a structure like regex_t should code the data structure length on the DS line of the data structure definition.
D regex_t DS 656 ALIGN QUALIFIED BASED(TEMPLATE) D etc
typedef union { type1 name1; type2 name2; ... } XXX;
For example
typedef union xxx D XXX DS ALIGN { D i 10i 0 overlay(XXX:1) int i; D d 8f overlay(XXX:1) double d; D name 10a overlay(XXX:1) char name[10]; D c 1a overlay(XXX:1) char c; } XXX;
Here are a few examples. Try them yourself before reading the answers.
1. typedef int x1; 2. typedef struct { int i; char c; char x[15]; } x2; 3. typedef _Packed struct { int i[5]; double d; } x3; 4. typedef union { short s; float f; double d[2]; } x4; 5. typedef x4 *x5;
When you see a C prototype like
void fn(void);you normally assume that the external name for this function is 'fn', so you normally code EXTPROC('fn').
Unfortunately, it is possible that the external name is something other than 'fn'. The way that C specifies that the external name is different is by using #pragma map.
#pragma map (fn, "fn_v2") void fn(void);The #pragma map says that the function named "fn" should actually be named "fn_v2" externally. (#pragma map can also be used to rename exported data fields.)
There is no guarantee that the #pragma map is adjacent to the prototype in the C header file. You should search the entire header file.
You may see more than one #pragma map for the same function. In that case, there will be preprocessor directives to condition which one is in effect. There might also be preprocessor directives conditioning a single #pragma map; in that case, it might be correct to use the original non-mapped name.
You might be able to find out which external name to use from the documentation, or you might be able to guess which one you should use. If you can't determine the right name to use, and if you have access to a C compiler, you can compile a C module using that function, compiling it with whatever compiler options are recommended for that function. Then do DSPMOD DETAIL(*IMPORT) and see what function the C module will call.
typedef struct { int i; char c; char x[15]; } x2; Pre-V5R1 RPG: (also see V5R1 version) D x2 DS ALIGN D x2_i 10I D x2_c 1A D x2_x 15A
typedef struct { int i; char a[10]; } TTT; void fn (TTT *t); * Here's the RPG version of the "typedef" and the prototype * with some example code calling the function D TTT DS ALIGN QUALIFIED D BASED(template) D i 10I D a 10A D myTTT DS LIKEDS(TTT) D fn PR EXTPROC(*CWIDEN : 'fn') D t LIKEDS(TTT) /free myTTT.i = 25; myTTT.a = "abcde"; fn (myTTT);
Here are a couple of challenging examples. Try them yourself before reading the answers.
1. /*-----------------------------------------------------*/ /* fn1 */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Input: p1: null-terminated string */ /* Input: p2: 10 bytes, right-adjusted, blank filled */ /* In: p3: 1-byte character */ /* */ /* Optional parameters: */ /* */ /* Input: p4: int */ /* Input: p5: int */ /* */ /* Returns: short int */ /*-----------------------------------------------------*/ short fn1 (char *p1, char *p2, char p3, ...) 2. /*-----------------------------------------------------*/ /* fn2 */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Output: p1: integer */ /* Input: p2: array of integers */ /* Input: p3: array of pointers to character */ /* */ /* Returns: none */ /*-----------------------------------------------------*/ void fn2 (int *p1, int *p2, char *p3[]); 3. /*-----------------------------------------------------*/ /* num_recs */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Input: filename: 10 bytes, blank-padded */ /* */ /* Returns: int */ /* */ /* Notes: */ /* 1. Compiling with DEFINE(DEBUG) will use a */ /* version of this function that outputs */ /* debugging information to stdout. */ /*-----------------------------------------------------*/ #ifdef DEBUG #pragma map(num_recs, "num_recs_debug") #endif int num_recs (char filename[]);
1. double sin ( double ); D sin PR 8F EXTPROC('sin') D 8F VALUE 2. struct s { int i; char c; }; void fn ( float *a, struct s b, struct s *c, unsigned short *d); D s DS ALIGN D s_i 10I D s_c 1A D fn PR EXTPROC('fn') D a 4F D b VALUE LIKE(s) D c LIKE(s) D d 5U 0
1. typedef int x1; D x1 PR 10i 0 2. typedef struct { int i; char c; char x[15]; } x2; D x2 DS ALIGN D i 10I D c 1A D x 15A 3. typedef _Packed struct { int i[5]; double d; } x3; D x3 DS D i 10I DIM(5) D d 8F 4. typedef union { short s; float f; double d[2]; } x4; D x4 DS ALIGN D s 5I DIM(5) D f 4F D d 8F DIM(2) 5. typedef x4 *x5; D x5 S *
/*-----------------------------------------------------*/ /* fn1 */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Input: p1: null-terminated string */ /* Input: p2: 10 bytes, right-adjusted, blank filled */ /* In: p3: 1-byte character */ /* */ /* Optional parameters: */ /* */ /* Input: p4: int */ /* Input: p5: int */ /* */ /* Returns: short int */ /*-----------------------------------------------------*/ short fn1 (char *p1, char *p2, char p3, ...) * V5R1+ solution D fn1 PR 5I 0 EXTPROC(*CWIDEN1 : 'fn1') D p1 * VALUE OPTIONS(*STRING) 2 D p2 10A OPTIONS(*RIGHTADJ) CONST 3 D p3 1A VALUE 4 D p4 10I 0 VALUE OPTIONS(*NOPASS) 5 D p5 10I 0 VALUE OPTIONS(*NOPASS) * Pre-V5R1 solution D fn1 PR 10I10 EXTPROC('fn1') D p1 * VALUE OPTIONS(*STRING) 2 D p2 10A OPTIONS(*RIGHTADJ) CONST 3 D p3 10U 0 VALUE 4 D p4 10I 0 VALUE OPTIONS(*NOPASS) 5 D p5 10I 0 VALUE OPTIONS(*NOPASS)
Notes on the answer to question 1:
/*-----------------------------------------------------*/ /* fn2 */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Output: p1: integer (length of the second parm) */ /* Input: p2: array of integers */ /* Input: p3: array of pointers to null-terminated */ /* strings. The final pointer must be */ /* null. */ /* Returns: none */ /*-----------------------------------------------------*/ void fn2 (int *p1, int *p2, char *p3[]); D fn1 PR EXTPROC(*CWIDEN: 'fn2') D p1 10I 0 D p2 10I 0 DIM(32767) 1 D CONST D p3 * DIM(32767)2 D OPTIONS(*VARSIZE)
Notes on the answer to question 2:
/*-----------------------------------------------------*/ /* num_recs */ /*-----------------------------------------------------*/ /* Required parameters: */ /* */ /* Input: filename: 10 bytes, blank-padded */ /* */ /* Returns: int */ /* */ /* Notes: */ /* 1. Compiling with DEFINE(DEBUG) will use a */ /* version of this function that outputs */ /* debugging information to stdout. */ /*-----------------------------------------------------*/ #ifdef DEBUG #pragma map(num_recs, "num_recs_debug") #endif int num_recs (char filename[]); There are two ways to handle conditionally picking up one name or the other: 1. Condition the EXTPROC keyword: D num_recs PR /IF DEFINED(DEBUG) 1 D EXTPROC(*CWIDEN : 'num_recs_debug') /ELSE D EXTPROC(*CWIDEN : 'num_recs') /ENDIF D filename 10A CONST 2. Condition the definition of a named constant used on the EXTPROC keyword. /IF DEFINED(DEBUG) D num_recs_name... D C 'num_recs_debug' /ELSE D num_recs_name... D C 'num_recs' /ENDIF D num_recs PR EXTPROC(*CWIDEN : num_recs_name) D filename 10A CONST
Notes on the answer to question 3: