gtpa2m28Application Programming

Special TPF Considerations

There are some characteristics of the TPF operating system that will affect your programming.

Coding main Functions

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.

Building and Loading DLMs Containing a main Function

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.

Defining a main Function

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:

Calling a DLM Containing a main Function

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:

Passing Arguments to main

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

command
A string that creates a new process, giving the program name and any additional parameters and standard stream redirections associated with the new process.

prog
The name of the TPF program segment that contains the main function.

redirection
Specification of a file that is to be opened as one of the standard streams.

< or 0<
Indicates redirection of the stdin stream.

> or 1>
Indicates redirection of the stdout stream. If the file exists, it is truncated to zero bytes.

2>
Indicates redirection of the stderr stream. If the file exists, it is truncated to zero bytes.

>> or 1>>
Indicates redirection of the stdout stream without truncation and with output appended to the end of the file.

2>>
Indicates redirection of the stderr stream without truncation and with output appended to the end of the file.

pathname
The path to the file from or to which the stream will be redirected.

parameter
A blank delimited substring of the command string that contains no blank characters and cannot be parsed as a redirection. Each parameter is passed as an element of the argv vector to the main function in program name.

The TPF system:

I/O Stream Pipes

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.

Example of Calling a DLM That Contains a main Function

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
Note:
In the previous example, program WXYZ inherits a copy of the ABCD environment list, but changes that WXYZ makes to its environment list are not copied back to the ABCD environment list.
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:

  1. Code a statement in SIP skeleton IBMPAL for each new external function. Use the format for a transfer vector as described in TPF System Installation Support Reference.
  2. Create a new version of the allocator.

Example of Creating an ECB That Enters a DLM That Contains a main Function

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.

Coding C++ Applications

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).

main or extern "C" Requirement

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)
     {
     ...
     ...
     }

C++ Exceptions

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.

Note:
Do not use C++ exceptions in applications that have parts written in (TARGET)TPF.
If a TARGET(TPF) program is dropped from a nesting level during exception processing, a SNAP system error is taken and the application continues to run; however, application problems may occur if the application calls a TARGET(TPF) program after the exception is processed.

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.

Note:
For this reason, do not write applications to use both signals and exceptions.

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

Exporting is a DLL concept. The following are ways to export functions and variables:

Reentrant Programming

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.

Note:
DLL and C++ applications can pass function pointers between themselves.
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.

Standard TPF Program Sizes

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:

  • Ensure that the appropriate set of header files is duplicated in the new segments.
  • Ensure that any required function prototypes and structure declarations are present in the new segments. Prototypes for static functions must be modified to reflect the fact that they now reside in a different source module and are, therefore, external.
  • There can be certain static functions that are called by both the functions being placed in the new segment, and the functions that remain in the old; these must be duplicated in both segments.
#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.

Static Storage Considerations

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).)