gtpa2m28 | Application Programming |
There are some characteristics of the TPF operating system that will affect your programming.
The TPF system supports coding main functions in dynamic load modules (DLMs) and passing the standard C argc and argv parameters to them. A DLM can contain, at most, one main function. If a DLM does contain a main function, the main function is the DLM entry point.
There is no change to the DLM build or load process. DLMs that contain a main function are compiled, prelinked, link-edited, and loaded exactly the same way as DLMs have been before this support.
The TPF offline loader (TPFLDR) program, the DLM startup code (segment CSTRTD, which must be linked into every DLM, including those that contain a main function), and the TPF C run-time environment initialization code (segment CLMINT in the CISO library) detect the presence of the main function and manage it appropriately.
CLMINT also includes a function similar to a UNIX shell that parses command strings passed with the system function into argc and argv parameters for the main function. This same function will also parse a command string contained in a core block attached to data level 0 (D0) of the ECB that is passed by various ECB create functions.
The main function can be defined anywhere in a DLM. The definition of main should return an int, and take either no parameters:
int main(void) { /* code for main */ }
or two parameters:
These parameters are conventionally named argc and argv; for example:
int main(int argc, char **argv) { /* code for main */ }
The constraints on these variables are as follows:
The TPF system supports calling a DLM containing a main function through the system function which is a standard C library function. The system function creates a new ECB, which runs synchronously while the calling ECB waits. In UNIX terms, the ECB that calls the system function is the parent process; the created ECB is the child process. When the child process exits, its exit value is returned to the parent process as the system function's return value, and the parent process resumes running.
In addition, the TPF system supports calling a DLM containing a main function through several TPF-unique ECB create functions or macros. The supported functions and macros include:
When a process calls the system function and creates a child process, the TPF system parses the system command string into argc and argv parameters for the main function of the child process.
When a process calls one of the TPF-unique functions that will enter a DLM, the TPF system checks data level 0 (D0) of the ECB for a core block that contains the command string. If D0 is unoccupied, argc is set to 0 and argv[0] is set to the program name. However, if there is a core block at D0 of the ECB, the TPF system parses the command string, starting at byte 0 of the core block, into argc and argv parameters for the main function. The TPF system then releases the core block.
The TPF system uses the following format to parse command strings:
|
Where
The TPF system:
Many C run-time environments support piping the standard output (stdout) stream from one process to the standard input (stdin) stream of a second process. The TPF system does not accept command strings that contain vertical bars (|) (which are used by UNIX and other C run-time environments to indicate I/O stream pipes) when you are using the system function. If a string containing a vertical bar is passed to the system function, the system function returns -1 and sets errno to EINVAL. For more information about I/O stream pipes, see the information for the mkfifo, tpf_fork, and pipe functions in TPF C/C++ Language Support User's Guide.
If the following program ABCD:
/* Demonstration program ABCD */ #include <stdio.h> #define _POSIX_SOURCE #include <stdlib.h> int main(int argc, char **argv) { FILE *fp = fopen("wxyz.input", "w"); fputs("A message from ABCD.\n", fp); fputs("TPF now supports:\n\n", fp); fputs("-- int main(void);\n", fp); fputs("-- int main(int argc, char ** argv);\n", fp); fputs("-- environment variables, inherited through system();\n", fp); fputs("-- standard I/O stream redirection.\n", fp); fclose(fp); setenv("myname", argv[0], 1); printf("%s: My name is %s.\n", argv[0], getenv("myname")); printf("%s: Now I will execute program WXYZ.\n", argv[0]); printf("%s: WXYZ returned %d\n", argv[0], system("WXYZ <wxyz.input one two three")); printf("%s: My name is still %s\n", argv[0], getenv("myname")); remove("wxyz.input"); return 0; }
invokes the following program WXYZ:
/* Demonstration program WXYZ */ #include <stdio.h> #include <limits.h> #define _POSIX_SOURCE #include <stdlib.h> int main(int argc, char **argv) { int i; char buffer[256]; printf("%s: My parent's name is %s\n", argv[0], getenv("myname")); setenv("myname", argv[0], 1); printf("%s: My name is %s\n", argv[0], getenv("myname")); printf("%s: argc = %d\n", argv[0], argc); for (i = 0; i < argc; ++i) { printf("%s: argv[%d] = %s\n", argv[0], i, &cond. argv[i]); } for (i = 1; gets(buffer); ++i) { printf("%s: Line %d of stdin: %s\n", argv[0], i, buffer); } return 42; }
The output will then be:
ABCD: My name is ABCD. ABCD: Now I will execute program WXYZ. WXYZ: My parent's name is ABCD WXYZ: My name is WXYZ WXYZ: argc = 4 WXYZ: argv[0] = WXYZ WXYZ: argv[1] = one WXYZ: argv[2] = two WXYZ: argv[3] = three WXYZ: Line 1 of stdin: A message from ABCD. WXYZ: Line 2 of stdin: TPF now supports: WXYZ: Line 3 of stdin: WXYZ: Line 4 of stdin: -- int main(void); WXYZ: Line 5 of stdin: -- int main(int argc, char ** argv); WXYZ: Line 6 of stdin: -- environment variables, inherited through system(); WXYZ: Line 7 of stdin: -- standard I/O stream redirection. ABCD: WXYZ returned 42 ABCD: My name is still ABCD
TARGET(TPF) Restriction |
---|
While we are talking about functions, there is something else that you should keep in mind. If a function linkage is not explicitly specified (coded without static or extern specified), the default is extern. Although there is no requirement to explicitly type functions, it is recommended for TPF systems. If you change the declaration of a function from static to extern, you must change the allocator table so that external linkage is provided for the new entry point:
|
If the following program EFGH:
#include <tpfapi.h> #include <stdlib.h> #include <stdio.h> #define PROG_NAME "EFGH" #define COMMAND_STRING "STUV Parm#1 Parm#2 TESTParm" void EFGH (void) { int seconds; /* CRETC time interval */ char *coreBlock; /* pointer to core block */ char *action; /* action word passed by CRETC */ printf ("%s: Creating ECB to enter DLM main.\n", PROG_NAME); /*****************************************************/ /* Release data level 3, if held. Then get a new */ /* core block which will hold command string for */ /* the main function. */ /*****************************************************/ crusa (1, D3); coreBlock = (char *)getcc(D3, GETCC_TYPE, L2); memset (coreBlock, 0x00, ecbptr()->ce1cc3); /*****************************************************/ /* Setup the command string for the main function. */ /*****************************************************/ strcpy (coreBlock, COMMAND_STRING, strlen(COMMAND_STRING)); /*****************************************************/ /* Create a time-initiated ECB. */ /*****************************************************/ seconds = 10; action = "TPF0"; cretc_level (CRETC_SECONDS, STUV, seconds, action, D3); /*****************************************************/ /* Print completion message. */ /*****************************************************/ printf ("%s: ECB will be created in %d seconds.\n", PROG_NAME, seconds); exit(0); }
invokes the following program STUV:
#include <stdlib.h> #include <stdio.h> #define PROG_NAME "STUV" int main (int argc, char **argv) { int i; /* Loop counter */ char *action; /* Action code passed by caller */ printf ("%s: New ECB created successfully.\n", PROG_NAME); action = (char *)&ecbptr;()->ebw000; action[4] = '\0'; /*****************************************************/ /* Print the action code which was passed by caller. */ /*****************************************************/ printf ("%s: Action code = %s.\n", PROG_NAME, action); /*****************************************************/ /* Print the name of our program and then loop */ /* through each of the parameters passed in the argv */ /* parameter. */ /*****************************************************/ printf ("%s: Our program name is %s.\n", PROG_NAME, argv[0]); for (i = 1; i < argc; i++) } printf ("%s: Parameter #%d is %s.\n", PROG_NAME, i, argv[i]); } /*****************************************************/ /* Exit this ECB. */ /*****************************************************/ printf ("%s: Created ECB is now exiting.\n", PROG_NAME); exit (0); }
The output will then be:
EFGH: Creating ECB to enter DLM main. EFGH: ECB will be created in 10 seconds. STUV: New ECB created successfully. STUV: Action code = TPF0. STUV: Our program name is STUV. STUV: Parameter #1 is Parm#1. STUV: Parameter #2 is Parm#2. STUV: Parameter #3 is TESTParm. STUV: Created ECB is now exiting.
This section describes some of the concepts you need to know when coding C++ applications. See the IBM C/C++ user's guide and programmer's guide for the System/390 platform used by your installation for a more thorough discussion about C++ and dynamic link libraries (DLLs).
DLL applications are required to have either main or an entry point with the same name as the load module. If your DLL application does not have main, C++ mangles the function name. You code an extern "C" linkage specification to produce an entry point with the same 4-character uppercase name as the load module.
The extern "C" linkage specification allows a C++ application entry point to be called through TPF enter/back services. This linkage specification also forces the linkage to the entry point of the load module to be C linkage instead of C++ linkage.
Only the entry point function must have the extern "C" linkage specification. Other functions in a C++ application do not need this linkage specification. A function in the C++ application that is called by another function in the same load module can have C++ linkage.
If you do not code the extern "C" linkage specification, the offline loader (TPFLDR) provides an error message that the entry point is not found in the program.
In the following example, the extern "C" linkage specification produces an entry point with the same name as the QZZ0 load module name. The call to ReadIt in EmpClass does not require this linkage specification.
class EmpClass { ... } extern "C" void QZZ0 (); { double raise; EmpClass *EmpPtr = new EmpClass[total_employees]; ... raise = EmpPtr[i].ReadIt(raise); ... } double & EmpClass::ReadIt (double & rate) { ... ... }
This section describes some of the TPF system considerations you need to know when coding C++ exceptions in your application. See the OS/390 C/C++ Language Reference for more information about general C++ exception handling.
The TPF system supports only application-defined exceptions. System errors and program checks are handled by the operating system and are not surfaced to the application.
Exceptions can be thrown across load module boundaries; for example, an exception can be thrown by a DLM but caught by another DLM that resides in the ECB program nesting level (PNL). All TPF programs that reside in the PNL between the current program and the program that contains a catch clause are dropped from the nesting level. TPF enter/back processing performs all clean up that is associated with dropping a program from a nesting level.
If an exception is thrown, but a catch clause in the application cannot be found to handle the exception, the standard behavior is that the terminate function is called. The default action of the terminate function is to call the abort function, which raises the SIGABRT signal; however, the terminate function is modified to produce a system error and exit the ECB instead of calling the abort function.
If you code a throw block in a destructor, the corresponding try and catch blocks must also be in the scope of the destructor or the results are unpredictable.
Exporting is a DLL concept. The following are ways to export functions and variables:
All TPF application programs must be reentrant. This means that a TPF application program must be coded as if it was being run simultaneously by more than one process. This is called parallel reentrancy. Furthermore, TPF applications must not leave anything behind that alters the path of a subsequent process. This is called serial reentrancy.
TPF systems provides 2 ways of defining data objects to be accessed by more than 1 program (globals).
Addresses can be passed between functions in the same DLM. Passing function pointers between DLMs can result in errors, because in most cases the static storage is not set up properly.
Programming Rule |
---|
Pass addresses between functions in the same C program. Do not pass function pointers between C programs. |
It is possible, though difficult, to write self-modifying code in C. Because of reentrancy requirements, this practice is not allowed in TPF systems.
ISO-C programs can exceed 4095 bytes (4KB). The load module representing an ISO-C program can be the largest size supported by the linkage editor. The load module is stored on online DASD in 4KB chained records. When loaded into main storage, contiguous storage is used.
TARGET(TPF) Restriction |
---|
TARGET(TPF) programs are restricted to 4KB blocks. If you need to break up an existing C source program into smaller segments, do the following:
|
#include <stdlib.h> #include <stdio.h> #define PROG_NAME "STUV" int main (int argc, char **argv) { int i; /* Loop counter */ char *action; /* Action code passed by caller */ printf ("%s: New ECB created successfully.\n", PROG_NAME); action = (char *)ecbptr()->ebw000; /*****************************************************/ /* Print the action code which was passed by caller. */ /*****************************************************/ printf ("%s: Action code = %s.\n", PROG_NAME, action); /*****************************************************/ /* Print the name of our program and then loop */ /* through each of the parameters passed in the argv */ /* parameter. */ /*****************************************************/ printf ("%s: Our program name is %s.\n", PROG_NAME, argv[0]); for (i = 1; i < argc; i++) { printf ("%s: Parameter #%d is %s.\n", PROG_NAME, i, argv[i]); } /*****************************************************/ /* Exit this ECB. */ /*****************************************************/ printf ("%s: Created ECB is now exiting.\n", PROG_NAME); exit (0); }
For planning purposes, it is a good idea to map out all of the functions in the original source module and determine which functions are called by other functions.
As described in Understanding High-Level Language Concepts in the TPF System, static storage is the storage required for static variables needed by a given ECB. Once acquired, this storage is not released until the ECB exits. (Static storage is not released with an ENTDC for ISO-C but it is released for TARGET(TPF).)