Introduction to API Vectors

The Rexx APIs operate through a set of interface vectors that define a set of interpreter services that are available. There are different interface vectors used for different contexts, but they use very similar calling concepts.

The first interface vector you'll encounter with the programming interfaces is the RexxInstance value returned by RexxCreateInterpreter. The RexxInstance type is defined as a struct when compiled for C code, or a C++ class when compiled for ++. The struct version looks like this:

struct RexxInstance_
    RexxInstanceInterface *functions;   // the interface function vector
    void *applicationData;              // creator defined data pointer

The field applicationData contains any value that was specified via the APPLICATION_DATA option on the RexxCreateInterpreter call. This provides easy access any application-specific data needed to interact with the interpreter. All other interface contexts will include a pointer to the RexxInstance structure, so it is always possible to recover this data pointer.

The functions field is a pointer to a second structure that defines the RexxInstance programming interfaces. The RexxInstance services are ones that may be called from any thread and in any context. The services are called using C function pointer fields in the interface structure. The RexxInstanceInterface looks like this:

typedef struct
    wholenumber_t interfaceVersion;    // The interface version identifier

    void        (RexxEntry *Terminate)(RexxInstance *);
    logical_t   (RexxEntry *AttachThread)(RexxInstance *, RexxThreadContext **);
    size_t      (RexxEntry *InterpreterVersion)(RexxInstance *);
    size_t      (RexxEntry *LanguageLevel)(RexxInstance *);
    void        (RexxEntry *Halt)(RexxInstance *);
    void        (RexxEntry *SetTrace)(RexxInstance *, logical_t);
} RexxInstanceInterface;

The first thing to note is the interface struct contains a field named interfaceVersion. The interfaceVersion field is a version marker that defines the services the called interpreter version supports. This interface version is incremented any time new functions are added to the interface. Using the interface version allows application code to reliably check that required interface functions are available.

The remainder of the fields are functions that can be called to perform RexxInstance operations. Note that the first argument to all of the functions is a pointer to a RexxInstance structure. A call to the InterpreterVersion API from C code would look like this:

size_t version = context->functions->InterpreterVersion(context);

When using C++ code, the RexxThreadContext struct has convenience methods that simplify calling these functions:

size_t version = context->InterpreterVersion();

Note that in the C++ call, it is no longer necessary to pass the RexxInstance as the first object. That's handled automatically be the C++ method.

The RexxThreadContext pointer returned from RexxCreateInterpreter() functions the same way. RexxThreadContext looks like this:

struct RexxThreadContext_
    RexxInstance *instance;             // the owning instance
    RexxThreadInterface *functions;     // the interface function vector

The RexxThreadContext struct contains an embedded RexxInstance pointer for the instance it is associated with. It also contains an interface vector for the functions available with a RexxThreadContext. The RexxThreadInterface vector has its own version identifier and function pointer for each of the defined services. The RexxThreadContext functions all require a RexxThreadContext pointer as the first argument. The RexxThreadContext struct also defines C++ convenience methods for accessing its own function and the functions for the RexxInstance as well. For example, to call the InterpreterVersion() API using a RexxThreadContext from C code, it is necessary to code

size_t version = context->instance->functions->InterpreterVersion(context->instance);

The C++ version is simply

// context is a RexxThreadContext *
size_t version = context->InterpreterVersion();

When the Rexx interpreter makes calls to native code routines and methods, or invokes exit handlers, the call-outs use context structures specific to the callout context. These are the RexxCallContext, RexxMethodContext, and RexxExitContext structures. Each structure contains a pointer to a RexxThreadContext instance that's valid until the call returns. Through the embedded RexxThreadContext, each call may use any of the RexxThreadContext or RexxInstance functions in addition to the context-specific functions. Each context defines C++ methods for the embedded RexxInstance and RexxThreadContext functions.

Note that the RexxInstance interface can be used at any time and on any thread. The RexxThreadContext returned by RexxCreateInterpreter() can only be used on the same thread as the RexxCreateInterpreter() call, but is not valid for use in the context of a method, routine, or exit call-out. In those contexts, the RexxThreadContext instance passed to the call-out must be used. A RexxThreadContext instance created for a call-out is only valid until the call returns to the interpreter.