Combining COBOL and C Programs

7.1.6. Combining COBOL and C Programs

Linkage between OpenCOBOL and C language programs is possible, but may require a little bit of special coding in one program or the other in order to meaningfully pass data between them. The issues involved deal predominantly with three topics, as follows. Each issue is discussed, with upcoming coding samples illustrating specifics as to how those issues are overcome in actual program code.

7.1.6.1. OpenCOBOL Run-Time Library Requirements

Like most other implementations of the COBOL language, OpenCOBOL utilizes a run-time library. When the first program unit executed in a given execution sequence is an OpenCOBOL program, any run-time library initialization will Like most other implementations of the COBOL language, OpenCOBOL utilizes a run-time library. When the first program unit executed in a given execution sequence is an OpenCOBOL program, any run-time library initialization will

7.1.6.2. String Allocation Differences Between OpenCOBOL and C

Both languages store strings as a fixed-length continuous sequence of characters. COBOL stores these character sequences up to a specific quantity limit imposed by the PICTURE cause of the data

item. For example: 01 LastName PIC X(15). There is never an issue of exactly what the length of a string contained in a USAGE DISPLAY data item is – there are always exactly how ever many characters as were allowed for by the PICTURE clause. In the example above,

“LastName” will always contain exactly fifteen characters; of course, there may be anywhere from 0 to 15 trailing SPACES as part of the current LastName value.

C actually has no “string” datatype – rather, it stores strings as an array of “char” datatype items where each element of the array is a single character. Being an array, there is an upper limit to how many characters may be stored in a given “string”. For example:

char lastName[15]; /* 15 chars: lastName[0] thru lastName[14] */

C provides a robust set of string-manipulation functions to copy strings from one char array to another, search strings for certain characters, compare one char array to another, concatenate char arrays and so forth. To make these functions possible, it was necessary to be able to define the logical end of a string. C accomplishes this via the expectation that all strings (char arrays) will be terminated by a NULL character (x’00’). Of course, no one forces a programmer to do this, but if [s]he ever expects to use any of the C standard functions to manipulate that string they had better be doing it.

So, OpenCOBOL programmers expecting to pass strings to or receive strings from C programs had best be prepared to deal with the null-termination issue.

7.1.6.3. Matching C Data Types with OpenCOBOL USAGEs

This is pretty simple, the OpenCOBOL and C programmer must just be aware of the following correspondence between C data types and COBOL USAGE specifications:

Figure 7-1 - C/OpenCOBOL Data Type Matches This COBOL USAGE…

Occupies this

Holds these numeric values…

And corresponds to this C

data type… (no PICTURE allowed)

space…

BINARY-CHAR

unsigned char BINARY-CHAR UNSIGNED BINARY-CHAR SIGNED

1 byte

0 to 255

signed char BINARY-SHORT

1 byte

-128 to +127

unsigned BINARY-SHORT UNSIGNED

2 bytes

0 to 65535

unsigned int unsigned short unsigned short int

BINARY-SHORT SIGNED

2 bytes

-32768 to +32767

int short short int signed int

This COBOL USAGE…

Occupies this

Holds these numeric values…

And corresponds to this C

data type… (no PICTURE allowed)

space…

signed short signed short int

BINARY-LONG

unsigned long BINARY-LONG UNSIGNED

4 bytes

0 to 4294967295

unsigned long int BINARY-LONG SIGNED

4 bytes

-2147483648 to +2147483647

long long int signed long signed long int

BINARY-C-LONG SIGNED

4 bytes or 8

(see the description of USAGE BINARY-C-LONG in

-9223372036854775808 to

Figure 5-10 )

unsigned long long BINARY-DOUBLE UNSIGNED

unsigned long long int BINARY-DOUBLE SIGNED

8 bytes

-9223372036854775808 to

long long int

signed long long int COMPUTATIONAL-1

4 bytes

-3.4 x 10 38 to +3.4 x 10 38 float

(six decimal digits of precision)

COMPUTATIONAL-2

8 bytes

-1.7 x 10 308 to +1.7 x 10 308

double

(15 decimal digits of precision)

N/A (no OpenCOBOL

long double equivalent)

12 bytes

-1.19 x 10 4932 to +1.19 x 10 4932

(18 decimal digits of precision)

There are other OpenCOBOL PICTURE/USAGE combinations that can define the same storage size and value range combinations, but (with the exception of COMP-1 and COMP-2), these are the ANSI2002 standard specifications for C- program data compatibility and OpenCOBOL programmers should get used to using them when data is being shared with C programs (they’re good documentation too, highlighting the fact that the data will be “shared” with a C program).

The minimum values shown for the various SIGNED integer USAGEs are appropriate for a computer system that uses 2s-complement representation for negative signed binary values (such as those CPUs typically found in Windows PCs).

A computer system using 1s-complement representation for negative signed binary values would have minimum values that are 1 greater (-127 instead of -128, for example).

7.1.6.4. OpenCOBOL Main Programs CALLing C Subprograms

Here are samples of an OpenCOBOL program that CALLs a C subprogram. Figure 7-2 - OpenCOBOL CALLing C

(maincob.cbl)

(subc.c)

This OpenCOBOL MAIN PROGRAM… …wants to CALL this C SUBPROGRAM IDENTIFICATION DIVISION.

#include <stdio.h>

PROGRAM-ID. maincob. DATA DIVISION.

int subc(char *arg1,

WORKING-STORAGE SECTION.

char *arg2,

01 Arg1 PIC X(7). unsigned long *arg3) { 01 Arg2 PIC X(7).

char nu1[7]="New1";

01 Arg3 USAGE BINARY-LONG.

char nu2[7]="New2";

PROCEDURE DIVISION.

printf("Starting subc\n");

000-Main.

printf("Arg1=%s\n",arg1);

DISPLAY 'Starting cobmain'.

printf("Arg2=%s\n",arg2);

MOVE 123456789 TO Arg3.

printf("Arg3=%d\n",*arg3);

STRING 'Arg1'

arg1[0]='X';

X'00'

arg2[0]='Y';

DELIMITED SIZE

*arg3=987654321;

INTO Arg1

return 2;

END-STRING.

STRING 'Arg2' X'00' DELIMITED SIZE INTO Arg2

END-STRING. CALL 'subc' USING BY CONTENT Arg1,

BY REFERENCE Arg2, BY REFERENCE Arg3.

DISPLAY 'Back'. DISPLAY 'Arg1=' Arg1. DISPLAY 'Arg2=' Arg2.

DISPLAY 'Arg3=' Arg3. DISPLAY 'Returned value='

RETURN-CODE. STOP RUN.

The idea is to pass two string and one full-word unsigned arguments to the subprogram, have the subprogram print them out, change all three and pass a return code of 2 back to the caller . The caller will then re-display the three arguments (showing changes only to the two BY REFERENCE arguments), display the return code and halt. While simple, these two programs illustrate the techniques required quite nicely.

Note how the COBOL program ensures that a null end-of-string terminator is present on both string arguments. Since the C program is planning on making changes to all three arguments, it declares all three as pointers in the

function header and references the third argument as a pointer in the function body. 33

33 It actually had no choice for the two string (char array) arguments – they must be defined as pointers in the function even though the function code references them without the leading “*” that normally signifies pointers.

These programs are compiled and executed as follows. The example assumes a UNIX system with an OpenCOBOL build that uses the native C compiler on that system; the technique works equally well regardless of which C compiler and which operating system you’re using.

$ cc –c subc.c $ cobc -x maincob.cbl subc.o $ maincob Starting cobmain Starting subc Arg1=Arg1 Arg2=Arg2 Arg3=123456789 Back Arg1=Arg1 Arg2=Yrg2 Arg3=+0987654321 Returned value=+000000002 $

Remember that the null characters are actually in the OpenCOBOL “Arg1” and “Arg2” data items. They don’t appear in the output, but they ARE there. When passing character strings to C programs, it’s probably a good idea to make a null-terminated copy of the string items and pass those copies to the C program.

As was discussed in section 6.7 , an OpenCOBOL CALL to a subprogram will need to specify the BY CONTENT clause to make an argument unchangeable by a subprogram if that subprogram is written in a language other than OpenCOBOL. When the CALLing and CALLed programs are both OpenCOBOL, the BY VALUE will be a faster alternative to BY CONTENT.

7.1.6.5. C Main Programs CALLing OpenCOBOL Subprograms

Now, the roles of the two languages in the previous section will be reversed, having a C main program execute an OpenCOBOL subprogram.

Figure 7-3 - C CALLing OpenCOBOL (mainc.c)

(subcob.cbl)

This C MAIN PROGRAM… …wants to CALL this OpenCOBOL SUBPROGRAM #include <libcob.h>

IDENTIFICATION DIVISION. #include <stdio.h>

PROGRAM-ID. subcob. DATA DIVISION.

int main (int argc, char **argv) {

LINKAGE SECTION.

int returnCode; 01 Arg1 PIC X(7). char arg1[7] = "Arg1";

01 Arg2 PIC X(7). char arg2[7] = "Arg2";

01 Arg3 USAGE BINARY-LONG. unsigned long arg3 = 123456789;

PROCEDURE DIVISION USING printf("Starting mainc...\n");

BY VALUE Arg1,

cob_init (argc, argv);

BY REFERENCE Arg2,

/* cob_init(0,NULL) if cmdline args not going BY REFERENCE Arg3. to COBOL */

000-Main.

returnCode = subcob(arg1,arg2,&arg3); DISPLAY 'Starting cobsub.cbl'. printf("Back\n");

DISPLAY 'Arg1=' Arg1. printf("Arg1=%s\n",arg1);

DISPLAY 'Arg2=' Arg2. printf("Arg2=%s\n",arg2);

DISPLAY 'Arg3=' Arg3. printf("Arg3=%d\n",arg3);

MOVE 'X' TO Arg1 (1:1). printf("Returned value=%d\n",returnCode);

MOVE 'X' TO Arg2 (1:1). return returnCode;

MOVE 987654321 TO Arg3. } MOVE 2 TO RETURN-CODE. GOBACK.

Since the C program is the one that will execute first, before the OpenCOBOL subroutine, the burden of initializing the OpenCOBOL run- time environment lies with that C program; it will have to invoke the “cob_init” function, which is part of the “libcob” library. The two required C statements are shown, highlighted in boldface.

The arguments to the “cob_init” routine are the argument count and value parameters passed to the main function when the program began execution. By passing them into the OpenCOBOL subprogram, it will be possible for that OpenCOBOL program to retrieve the command line or individual command- line arguments. If that won’t be necessary, “cob_init(0,NULL);” could be specified instead.

Since the C program wants to allow “arg3” to be changed by the subprogram, it prefixes it with a “&” to force a CALL BY REFERENCE for that argument. Since “arg1” and “arg2” are strings (char arrays), they are automatically passed by reference.

Here’s the output of the compilation process as well as the program’s execution. The example assumes a Windows system with an OpenCOBOL build that uses the GNU C compiler on that system; the technique works equally well regardless of which C compiler and which operating system you’re using.

C:\Users\Gary\Documents\Programs> cobc -S subcob.cbl C:\Users\Gary\Documents\Programs> gcc mainc.c subcob.s –o mainc.exe -llibcob C:\Users\Gary\Documents\Programs> mainc.exe Starting mainc... Starting cobsub.cbl Arg1=Arg1 Arg2=Arg2 Arg3=+0123456789 Back Arg1=Xrg1 Arg2=Xrg2 Arg3=987654321 Returned value=2 C:\Users\Gary\Documents\Programs>

Note that even though we told OpenCOBOL that the 1 st argument was to be BY VALUE, it was treated as if it were BY REFERENCE anyway. String (char array) arguments passed from C callers to OpenCOBOL subprograms will be modifiable by the subprogram. It’s best to pass a copy of such data if you want to ensure that the subprogram doesn’t change it.

The third argument is differen t, however. Since it’s not an array you have the choice of passing it either BY

REFERENCE 34 or BY VALUE 35 .

34 Use “&” with the argument in the C calling program; specify the argument as BY REFERENCE in the COBOL subprogram

35 Don’t use “&” with the argument in the C calling program; specify the argument as BY VALUE in the COBOL subprogram