Chapter 9. Rexx C++ Application Programming Interfaces

Table of Contents
Rexx Interpreter API
Data Types Used in APIs
Introduction to API Vectors
Threading Considerations
Garbage Collection Considerations
Rexx Interpreter Instance Interface
Rexx Thread Context Interface
Rexx Method Context Interface
Rexx Call Context Interface
Rexx Exit Context Interface
Building an External Native Library
Rexx Exits Interface
Command Handler Interface
Rexx Interface Methods Listing

This chapter describes how to interface applications to Rexx or extend the Rexx language by using Rexx C++ application programming interfaces (APIs). As used here, the term application refers to programs written in C++.

The features described here let a C++ application extend many parts of the Rexx language or extend an application with Rexx. This includes creating handlers for Rexx methods, external functions, and system exits.

Rexx methods

are methods for Rexx classes written in C++. The methods reside in dynamically loaded external shared libraries.

Functions

are function extensions of the Rexx language written in C++. Like the native methods, functions are packaged in external libraries. Functions can be general-purpose extensions or specific to an application.

Command Handlers

are programmer-defined handlers for named command environments. The application programmer can tailor the Rexx interpreter behavior by creating named command environments to interfacing with application environments.

System exits

are programmer-defined variations of the interpreter. The application programmer can tailor the Rexx interpreter behavior by using the defined exit points to control Rexx resources.

Methods, functions, system exit handlers, and command handlers have similar coding, compilation, and packaging characteristics.

In addition, applications can call methods defined of Rexx objects and execute them from externally defined methods and functions.

Rexx Interpreter API

Rexx programs run in an environment controlled by an interpreter instance. An interpreter instance environment is created with an enable set of exit handlers and a customized environment. An instance may have multiple active threads and each interpreter instance has a unique version of the .local environment directory, allowing programs to run with some degree of isolation.

If you use the older RexxStart() API to run a Rexx program, the Rexx environment initializes, runs a single program, and the environment is terminated. With the RexxCreateInterpreter() API, you have fine grain control over how the environment is used. You are able to create a tailored environment, perform multiple operations (potentially, on multiple threads), create objects that persist for longer than the life of a single program, etc. An application can create an interpreter instance once, and reuse it to run multiple programs.

Interpreter environments are created using the RexxCreateInterpreter() API:

RexxInstance *instance;
RexxThreadContext *threadContext;
RexxOption options[25];

if (RexxCreateInterpreter(&instance, &threadContext, options)) {
…
}

Once you've created an interpreter instance, you can use the APIs provided by the RexxInstance or RexxThreadContext interface to perform operations like running programs, loading class packages, etc. For example, the following code will run a program using a created instance, checking for syntax errors upon completion:

    // create an Array object to hold the program arguments
    RexxArrayObject args = threadContext->NewArray(instanceInfo->argCount);
    // we're passing a variable number of arguments, so we need to create
    // String objects and insert them into the array
    for (size_t i = 0; i < argCount; i++)
    {
        if (arguments[i] != NULL)
        {
            // add the argument to the array, if specified.  Note that ArrayPut() requires an
            // index that is origin-1, unlike C arrays which are origin-0.
            threadContext->ArrayPut(args, threadContext->String(arguments[i]), i + 1);
        }
    }

    // call our program, using the provided arguments.
    RexxObjectPtr result = threadContext->CallProgram("myprogram.rex", args);
    // if an error occurred, get the decoded exception information
    if (threadContext->CheckCondition())
    {
        RexxCondition condition;

        // retrieve the error information and get it into a decoded form
        RexxDirectoryObject cond = threadContext->GetConditionInfo();
        threadContext->DecodeConditionInfo(cond, &condition);
        // display the errors
        printf("error %d: %s\n%s\n", condition.code, threadContext->CString(condition.errortext),
           threadContext->CString(condition.message));
    }
    else
    {
        // Copy any return value as a string
        if (result != NULLOBJECT)
        {
            CSTRING resultString = threadContext->CString(result);
            strncpy(returnResult, resultString, sizeof(returnResult));
        }
    }
    // make sure we terminate this first
    instance->Terminate();

The example above creates a Rexx String object for each program argument stores them in a Rexx array. It then uses CallProgram() to call "myprogram.rex", passing the array object as the program arguments. On return, if the program terminated with a Rexx SYNTAX error, it displays the error message to the console. Finally, if the program exited normally and returned a value, the ASCII-Z value of that result is copied to a buffer. As a final step, the interpreter instance is destroyed once we're finished using it.

RexxCreateInterpreter

RexxCreateInterpreter creates an interpreter instance and an associated thread context interface for the current thread.

RexxInstance *instance;
RexxThreadContext *threadContext;
RexxOption options[25];

if (RexxCreateInterpreter(&instance, &threadContext, options)) {
…
}

Arguments

instance

The returned RexxInstance interface vector. The interface vector provides access to APIs that apply to the global interpreter environment.

threadContext

The returned RexxThreadContext interface vector for the thread that creates the interpreter instance. The thread context vector provides access to thread-specific services.

options

An array of RexxOption structures that control the interpreter instance initialization. See Interpreter Instance Options for details on the available options.

Returns

1 (TRUE) if the interpreter instance was successfully created, 0 (FALSE) for any failure to create the interpreter.

Interpreter Instance Options

The third argument to RexxCreateInterpreter is an options array that sets characteristics of the interpreter instance. The options argument points to an array of RexxOption structures, and can be NULL if no options are required. Each RexxOption instance contains information for named options that can be specified in any order and even multiple times. The oorexxapi.h include file contains a #define for each option name. The information required by an option varies with each option type, and is specified using a ValueDescriptor struct to handle a variety of data types. An entry with a NULL option name terminates the option list. The available interpreter options are:

INITIAL_ADDRESS_ENVIRONMENT

Contains the ASCII-Z name of the initial address environment that will be used for all Rexx programs run under this instance.

RexxOption options[2];

options[0].optionName = INITIAL_ADDRESS_ENVIRONMENT;
options[0].option = "EDITOR";
options[1].optionName = NULL;
APPLICATION_DATA

Contains a void * value that will be stored with the interpreter instance. The application data can be retrieved using the GetApplicationData() API. The application data pointer allows methods, functions, exits, and command handlers to recover access to globally defined application data.

RexxOption options[2];

options[0].optionName = APPLICATION_DATA;
options[0].option = (void *)editorInfo;
options[1].optionName = NULL;
EXTERNAL_CALL_PATH

Contains an ASCII-Z string defining an additional search path that is used when searching for Rexx program files. The call path string uses the format appropriate for the host platform environment. On Windows, the path elements are separated by semicolons (;). On Unix-based systems, a colon (:) is used.

RexxOption options[2];

options[0].optionName = EXTERNAL_CALL_PATH;
options[0].option = myCallPath;
options[1].optionName = NULL;
EXTERNAL_CALL_EXTENSIONS

Contains an ASCII-Z string defining a list of extensions that will be used when searching for Rexx program files. The specified extensions must include the extension ".". Multiple extensions are separated by a comma (,).

RexxOption options[2];

options[0].optionName = EXTERNAL_CALL_EXTENSIONS;
options[0].option = ".ed,.mac";  // add ".ed" and ".mac" to search path.
options[1].optionName = NULL;
LOAD_REQUIRED_LIBRARY

Specifies the name of an external native library that will be loaded once the interpreter instance is created. The library name is an ASCII-Z string with the library name in the same format used for ::REQUIRED LIBRARY. Multiple libraries can be loaded by specifying this option multiple times.

RexxOption options[2];

options[0].optionName = LOAD_REQUIRED_LIBRARY;
options[0].option = "rxmath";
options[1].optionName = NULL;
REGISTER_LIBRARY

Specifies a package that will be registerd with the Rexx environment without loading an external library. The library is specified with a RexxLibraryPackage structure that gives the library name and a pointer to the associated RexxPackageEntry table that describes the package contents. The library name is an ASCII-Z string with the library name in the same format used for ::REQUIRED LIBRARY. Multiple libraries can be registered by specifying this option multiple times.

RexxOption options[2];
RexxLibraryPackage package;

package.registeredName = "mypackage";
package.table = packageTable;

options[0].optionName = REGISTER_LIBRARY;
options[0].option = (void *)&package;
options[1].optionName = NULL;
DIRECT_EXITS

Specifies a list of system exits that will be used with this interpreter instance. The exits are a list of RexxContextExit structs. Each enabled exit is specified in a single RexxContextExit struct that identifies exit type and handler entry point. The list is terminated by an instance using an exit type of 0. The direct exits are called using the RexxExitContext calling convention. See Rexx Exits Interface for details.


RexxContextExit exits[2];
RexxOption options[2];


exits[0].handler = functionExit;
exits[0].sysexit_code = RXOFNC;
exits[1].sysexit_code = 0;

options[0].optionName = DIRECT_EXITS;
options[0].option = (void *)exits;
options[1].optionName = NULL;
DIRECT_ENVIRONMENTS

Registers one or more subcommand handler environments with the interpreter instance. The handlers are a list of RexxContextEnvironment structs. Each enabled handler is specified in a single RexxContextEnvironment struct identifying the handler name and entry point. The list is terminated by an instance using a handler name of NULL. The direct environment handlers are called using the calling convention described in Command Handler Interface.


RexxContextEnvironment environments[2];
RexxOption options[2];


environments[0].handler = editorHandler;
environments[0].name = "EDITOR";
environments[1].name = NULL;

options[0].optionName = DIRECT_ENVIRONMENTS;
options[0].option = (void *)environments;
options[1].optionName = NULL;
REGISTERED_EXITS

Specifies a list of system exits that will be used with this interpreter instance. The exits are a list of RexxContextExit structs. Each enabled exit is specified in a single RexxContextExit struct identifying the type of the exit and the name of the registered exit handler. The list is terminated by an instance using an exit type of 0. The registered exits are called using the RexxExitHandler calling convention. See Registered System Exits Interface for details.


RXSYSEXIT exits[2];
RexxOption options[2];


exits[0].sysexit_name = "MyFunctionExit";
exits[0].sysexit_code = RXOFNC;
exits[1].sysexit_code = 0;

options[0].optionName = REGISTERED_EXITS;
options[0].option = (void *)exits;
options[1].optionName = NULL;
REGISTERED_ENVIRONMENTS

Registers one or more subcommand handler environments with the interpreter instance. The handlers are a list of RexxRegisteredEnvironment structs. Each enabled handler is specified in a single RexxRegisteredEnvironment struct identifying the name of the environment and the registered subcom handler name. The list is terminated by an instance using a handler name of NULL. The direct environment handlers are called using the calling convention described in Subcommand Interface.


RexxRegisteredEnvironment environments[2];
RexxOption options[2];


environments[0].registeredName = "MyEditorName";
environments[0].name = "EDITOR";
environments[1].name = NULL;

options[0].optionName = REGISTERED_ENVIRONMENTS;
options[0].option = (void *)environments;
options[1].optionName = NULL;