This chapter describes the calling of C programs from COBOL programs, and the calling of COBOL programs and entry points from C functions. All references to C can also be taken to mean any other high-level languages that conform to C calling conventions, such as C++ code contained within the C++ syntax:
extern "C" { ... }.
This section describes how to call and cancel C programs from COBOL programs.
A COBOL program can call a C function using the COBOL CALL syntax. For example:
CALL "c_func" [USING ...] [RETURNING ...]
where c_func is a C function. Any USING items are passed as arguments to the C function. Any RETURNING item contains the return code from the C function.
Refer to the chapter The COBOL Interfacing Environment for more details.
Canceling a COBOL program ensures that the next time it is referenced it is in its initial state. However, this does not necessarily happen when a non-COBOL program is canceled. Any tidying up of resources (memory, open files, and so on) used by the non-COBOL program depends on the language used to create the program and the various options associated with its compiler.
When you cancel a dynamically called COBOL or non-COBOL program, the memory used by the program is freed. Canceling programs that are statically linked into the executable does not free any memory.
The demonstration program cobmain1.cbl can be found using Infomgr. It demonstrates a COBOL program calling C and C++ functions. It shows how to:
Server Express provides a number of library routines that enable you to mix C and COBOL programs. These functions are intended primarily for use in C programs.
The routines are prototyped in C header files, as indicated below, and are located in the $COBDIR/include directory.
The routines use portable data types, which have the naming convention cobtype_t. Definitions of these data types can be found in the cobtypes.h header file in $COBDIR/include.
A C function can call a COBOL entry point in the same way as it would call another C function. For example, the following demonstrates C code which calls a COBOL entry point, cobep, using two arguments:
cobep(arg1, arg2);
A C function can call, and cancel, COBOL programs and entry points using the cobcall(), cobcancel() and cobfunc() routines, as described in the sections below.
Before calling a COBOL program, you must call cobinit(). In a threaded environment where you don't know if you're the first thread or not, each thread should call cobinit().
If your application has a C main() and the command line needs to be accessed from COBOL, then cobcommandline() must be called.
Before exiting a thread that has called a COBOL program, but which was not created via COBOL multi-threading syntax or library routines, you must call cobthreadtidy().
Before exiting an application that has called a COBOL program, you must call either cobtidy() or cobexit().
If the COBOL program and the C function that calls it are not contained in a callable shared object, nor linked together in a system executable file, then:
cob ... -d cobep
which searches for cobep on disk (that is, for cobep.so, cobep.gnt, and cobep.int).
The COBOL run-time system sets the terminal to a mode suitable for COBOL. This means that any calls to the C library routine system() might fail because they do not reset the terminal settings before the call, or set them back after the call. You should replace your calls to system() with calls to the SYSTEM() C function.
All the C routines for calling COBOL are described in Reference Help topics.
The demonstration programs cmain1.c and cxxmain1.C can be found using Infomgr. They demonstrate a C and C++ program calling COBOL. They show how to:
This section describes the routines that enable a C program to safely access data declared within COBOL programs.
A COBOL record layout is similar to a C structure containing arrays of characters. When the Compiler assigns a COBOL record layout, it does not take into account the alignment of binary or pointer data items, or byte ordering of binary (COMP-X) data items, required by any specific hardware platform; because of this, COBOL applications are extremely portable. However, this portability can cause problems in mixed-language applications. C structure layout is not as rigidly defined as COBOL record layout and varies quite widely from operating system to operating system. For example, in COBOL the structure:
01 mystruct is typedef. 05 mystruct-key-1 pic x occurs 2. 05 mystruct-bin-1 pic x(4) comp-5.
is six bytes long, no matter what platform it is compiled on. However, with the C compilers provided on most operating systems, a straightforward representation of this record layout in C would give different results. For example, :
struct mystruct { char mystruct_key_1[2]; long mystruct_bin_1; };
would result in a structure eight bytes long on most 32-bit operating systems, and 16 bytes long on most 64-bit operating systems, due to alignment padding. A better solution, because it is portable, is to represent the COBOL record mystruct using the C structure:
struct mystruct { char mystruct_key_1[2]; char mystruct_bin_1[4]; };
To enable conversion of COBOL data records into the C data types for a mixed-language application, your Server Express product contains a C include file $COBDIR/include/cbltypes.h which contains many of the basic types used in COBOL applications and in our CBL_ library routine interfaces. These types directly correspond to those types contained in the copyfile $COBDIR/cpylib/cbltypes.cpy.
Revisiting the example from section 11.3.4.1, the following COBOL record layout:
01 mystruct is typedef. 05 mystruct-key-1 cblt_x1 occurs 2. *> aka. "PIC X" 05 mystruct-bin-1 cblt_x4_comp5. *> aka. # "PIC X(4) COMP-5"
can be correctly represented in C, using $COBDIR/include/cbltypes.h, as:
#include "cbltypes.h" struct mystruct { cbl_x1_t mystruct_key_1[2]; cbl_x4_comp5_t mystruct_bin_1; };
The type name conventions used in cbltypes.h, are intended to naturally reflect the PICTURE strings of the corresponding COBOL type definition. The basic types provided in this include file are as follows:
cbl_x1_t cobol character position - basis for all types cbl_pointer_t Equivalent to: USAGE POINTER cbl_ppointer_t Equivalent to: USAGE PROCEDURE-POINTER cbl_sx1_comp5_t Equivalent to: PIC s9(n) COMP-5 where n=1 or 2 cbl_sx2_comp5_t Equivalent to: PIC s9(n) COMP-5 where n=3 or 4 cbl_sx4_comp5_t Equivalent to: PIC s9(n) COMP-5 where n=7 through 9 cbl_sx8_comp5_t Equivalent to: PIC s9(18) COMP-5 cbl_x1_comp5_t Equivalent to: PIC X(1) COMP-5 cbl_x2_comp5_t Equivalent to: PIC X(2) COMP-5 cbl_x4_comp5_t Equivalent to: PIC X(4) COMP-5 cbl_x8_comp5_t Equivalent to: PIC X(8) COMP-5 cbl_x1_compx_t Equivalent to: PIC X(1) COMP-X cbl_x2_compx_t Equivalent to: PIC X(2) COMP-X cbl_x4_compx_t Equivalent to: PIC X(4) COMP-X cbl_x8_compx_t Equivalent to: PIC X(8) COMP-X
Even though you can portably declare COBOL data in the C language, you still need to operate on that data in a C context. To enable you to do this, Server Express includes the routines cobget and cobput, which enable many COBOL-based data types to be converted into a format suitable for use in the context of your C application component. These routines are described in Reference Help topics.
Server Express supports a number of screen handling routines that you can use from C programs. These enable the run-time system and run-time support libraries to handle output from both C programs called from COBOL programs and from ACCEPT/DISPLAY operations performed by the calling COBOL programs. Normally if any screen handling is carried out outside the control of COBOL (for example, under the control of C) COBOL is not aware of the output from the screen handling operation when control returns to COBOL. The effect of subsequent screen handling operations could thus be undefined. The routines provided enable you to avoid this problem.
The screen-handling routines are not portable to non-UNIX environments.
When using these routines you must include the header file cobscreen.h, which is located in the directory $COBDIR/include. This file defines the attributes you can use, the type (cobchtype) and declares any external functions the routine needs. The type cobchtype defines a two-byte pair, with the first byte containing a character and the second the attribute for that character.
The attribute byte of cobchtype, as used in some of these routines, takes the same values as the user attribute byte. By default the UNIX user attribute byte is monochrome with the attributes listed below. These values can change when generic attributes are used (from COBOL). See the topic Display Attribute Routines in your Library Routines for details of the COBOL system library routines that can be used to set attributes.
The default monochrome attributes are bit settings and can be combined by ORing them together. The following attribute bit values are defined in cobscreen.h:
Attribute | Description |
---|---|
A_NORMAL | Normal, no attribute |
A_BOLD | Bold or highlight |
A_UNDER | Underline |
A_REVERSE | Reverse |
A_BLINK | Blink |
A_DIM | Dim or lowlight |
These routines use their own "virtual cursor", set using cobmove(). The virtual cursor is thread-local; that is, each thread has its own virtual cursor.
These routines are described in Reference Help topics.
This section describes the calls you need to make if you want to use signal handling in your application, especially if you are mixing COBOL and C in your application.
The run-time system sets up default signal handlers for all signals whose operating system default action is to cause termination. These default handlers make sure that the COBOL system is properly cleaned up before terminating. Hence, any open files are closed (to prevent file corruption), the terminal is reset to a sane state, user posted exit and error procedures are executed, and so on.
The run-time system also uses signal handling to provide various functionality. For example, interrupt key detection, Animator Zoom interrupt key, Job Control, and so on.
This COBOL system provides two routines that you can use to complement or override the signal handling used by the run-time system. For more information see the Reference Help topics cobpostsighandler() and cobremovesighandler()
These routines allow multiple handlers for each signal and enable you to prioritize use of these handlers. You can use them from inside a C program only, not least because you need the C library include file <signal.h> for the signal values, which vary between platforms.
Use these routines instead of the operating system signal calls, (signal(), sigaction(), and so on) which provide only the ability to post a single signal handler for each call and so often prevent multiple applications being able to work together reliably.
For example, if you use one of the C library signal calls to post a signal handler before invoking a COBOL program, your signal handler might be overridden. Whereas, if you use one of the C library signal calls after invoking a COBOL program, the default run-time system handler, additional COBOL functionality and user posted signal handlers are all overridden, causing unexpected results.
You use the cobpostsighandler() routine to post signal handlers.
The run-time system also uses the cobpostsighandler() routine to post all of its signal handling. Default handlers are posted for all signals whose operating system default action causes termination. The default handlers either make sure that the COBOL system has been cleaned up before terminating, or cause the signal to be ignored. These default handlers assume that the signals were generated by the operating system due to a serious error or that a signal has been unexpectedly raised. Hence, they usually produce a run-time system message, such as 114 or 115.
If the same handler is posted twice at the same priority, and neither of them is removed using the cobremovesighandler() routine, the handler is executed twice (assuming the handler, and all handlers executed in between, do not return a value of zero).
If you try to post the same signal handler for the same signal at the same priority twice and the first has not been removed using cobremovesighandler(), then cobpostsighandler() returns the handle for the old handler and does not post a new one.
If two different handlers are posted with the same priority, the last one posted is executed first.
Once you have posted a handler, you do not need to repost it. The handler is called each time that signal is received (assuming that the first receipt of the signal does not cause termination). If you want to execute your handler only once, you must remove it, using the cobremovesighandler() routine, in your signal handler.
While a signal handler is processing a signal, the signal is blocked. This prevents recursion if the same signal is received in rapid succession
A signal handler must return normally. That is, you must not use any other function that terminates a signal handler function, such as longjmp() or coblongjmp(), and you must use the C syntax to return a value:
return(num);
If you do use another function to return, the signal remains blocked, preventing any further signals from being processed.
When the run-time system receives a signal it calls the handler posted with the highest priority for that signal. Your handler should take any appropriate action (such as setting a flag to be acted on later) and then return. If the handler returns a non-zero value, the run-time system calls the handler with the next highest priority for that signal. If the signal handler returns zero, the run-time system does nto call any further signal handlers
The run-time system posts its default signal handlers with priority 127. So, to override them, or to process your signal handler before them, post your handler with priority 128. Use a priority of 126 (or less) if you want to make sure that your handler is processed after the default run-time system handler (if the default run-time system handler does not exit).
The run-time system provides various functionality using signal handlers. These handlers are posted with a priority in the range of 129 to 139.
A signal handler should do very little. A signal can be generated at any time so the routine that was being executed could be in an "unsafe" state (eg. in the middle of modifying global variables) and so trying to execute it in your signal handler could cause problems. The routine being executed could be a C library routine such as malloc().
There are a small subset of C library routines that are safe to call from signal handlers. Do not try to call any other C library routines, or any COBOL routines, from your signal handler. See your system documentation for more information on the safe C library routines.
Some signals are generated by the operating system when a serious error has occurred. Catching these signals can cause unexpected and potentially dangerous results. You should not catch any of the following signals:
Preventing the run-time system from processing certain signals can break some run-time system functionality. If you catch any of these signals (with a priority of 140 or more), your signal handler must return a non-zero value so that the run-time system's handler is also executed. The signals concerned include:
Note: We reserve the right to change our default signal handlers or use extra signals in the future.
The following example illustrates setting up a handler for the SIGALRM signal.
#include <stdio.h> #include <signal.h> #include "cobsignal.h" #include "cobmain.h" #include "cobtypes.h" int mysigcatch(int sig, struct mf_siginfo *signf) { cobprintf("In new signal handler\n"); return(0); /* Do not process any further handlers */ } void mysigexit(cobsigtype_t sighand) { cobprintf("Cancelling handler\n"); cobremovesighandler(sighand); /* Remove the handler */ } main(int argc, char *argv[]) { cobsigtype_t sighandle; cobinit(); sighandle = cobpostsighandler(SIGALRM, 128, mysigcatch); cobfunc("cobolprog", argc, argv); /* call a cobol program using cobfunc function */ alarm(1); /* raise the signal */ pause(); /* wait for the signal */ sleep(4); mysigexit(sighandle); cobexit(0); /* Cobexit - close down COBOL run-time environment */ }
The cobpostsighandler() routine enables two or more handlers to be posted for the same signal. When that signal is generated by the operating system (such as a SIGINT generated when the interrupt key is pressed), each handler can take appropriate action and then let other handlers be executed (by returning a non-zero value). Alternatively, they can decide to make the signal "ignored" by returning a value of zero to prevent other handlers from being executed. However, so that your handler can work with other handlers for the same signal, a handler of priority 140 (or more) should return non-zero to let other handlers have the opportunity to also be notified. A handler of priority 128 can return zero if you do not want the run-time system default action to be executed (which may cause termination).
When you raise a signal (such as by using alarm() to raise a SIGALRM signal, or when raising a signal to a different run-unit or application), then it is probable that only your signal handler wants to be called and other posted signal handlers are not expecting the signal. Hence, in this case, you should set a global resource before raising the signal and then check that resource in your signal handler. If the resource is set (as expected), then the signal was raised by you, so you can take the appropriate action and return zero to prevent other handlers from being unexpectedly called. If the resource is unset, then someone else (or something else, such as the operating system) raised the signal and so you can take no action and should let other handlers execute by returning a non-zero value.
Examples of global resources are global variables (if in the same run-unit), shared memory, pipes, files, and so on. Hence, in the above example, we could have set a global variable before raising the alarm() signal. We would then need to check the global variable in the signal handler, mysigcatch(). If it was set, we would carry out our action, reset the variable and return zero. However, if the global variable had not been set, then some other code is likely to have raised the signal. Hence, we would not want to carry out our action and would instead need to return a non-zero value immediately. This allows the appropriate action to be taken in some other handler.
If you have any signal handlers that are posted using a C library signal call, such as signal() or sigaction(), change them to use the cobpostsighandler() routine. This is so that your handler and any default run-time system handler, additional COBOL functionality and user posted signal handlers can all co-exist without one overriding the other, causing unexpected results.
The cobpostsighandler() routine interface is very similar to the signal() interface. The two parameters you pass to signal() are also passed to the cobpostsighandler() routine, plus a priority.
The differences between signal() and the cobpostsighandler() routine are:
The return value from cobpostsighandler() is either a pointer or NULL. You use this pointer to remove your handler later using cobremovesighandler(). If the return value is NULL, the handler was not posted for the signal.
You do not need to repost a handler that you posted using cobpostsighandler() routine; it stays posted. If you want your handler to be executed only once, remove it using the cobremovesighandler() routine.
If you are using third party software that posts a signal handler using a C library signal routine, such as signal() or sigaction(), unexpected results may occur. For example, if the third party software posts its handler before COBOL has been invoked, then when COBOL is invoked the third party's handler might be overridden by a run-time system or COBOL handler. If the third party subsequently raises a signal, then a 115 error may occur.
To overcome this, you can use the run-time tunable signal_regime. This tunable prevents handlers from being posted for the specified signal if a signal handler is posted before the first COBOL invocation.
If handlers are prevented from being posted for a signal, due to this tunable, the cobpostsighandler() routine returns NULL. Hence, the default run-time system handlers and any additional COBOL functionality achieved using the signal is not available. It also means that you should check the value returned from any cobpostsighandler() routine calls for NULL.
We recommend that you use the signal_regime tunable only with third party software. You should always use the cobpostsighandler() routine in your own code. See the see the section signal_regime for details on how to set signal_regime.
Copyright © 2005 Micro Focus International Limited. All rights reserved.