Getting Started Documentation Glish Learn More Programming Contact Us
Version 1.9 Build 1556
News FAQ
Search Home


next up previous contents index
Next: The Proxy and ProxyStore Classes Up: The Glish Client Library Previous: An Example of a Client

Subsections



The Client Class

As discussed in § 15.2, page [*], above, Glish clients should construct a single Client object using the argc and argv with which the client program was invoked.

Standard Client Member Functions

Client objects provide the following public member functions:

Client( int &argc, char **argv, ShareType shared = NONSHARED )
creates a Client object using the given argc and argv variables, that upon return are updated to no longer include the special Glish arguments used to instruct the Client how to connect with the Glish interpreter.

The shared flag indicates if the client should be shared by more than one interpreter. By default, clients are non-shared, but this flag can be set to Client::USER, Client::GROUP or Client::WORLD to indicate that the client should be shared among interpreters started by a single user, users in the same group, or all users respectively.

If the program was not invoked by the Glish interpreter then the special arguments will be missing. The Client constructor detects this case and knows that the program is running stand-alone. If in this case the -glish argument is given, then the Client reads string-valued events from stdin and ``posts" outbound events to stdout. This behavior allows client programs to be debugged separate from running within Glish. A line such as:

    hello there how are you?
on stdin turns into a hello event with a value of a four-element string corresponding to "there how are you?".

virtual
~Client() terminates the client ``cleanly"; that is, it informs the Glish interpreter that the client terminated successfully and closes the connection between the client and the interpreter. If you want the client to indicate ``failure" instead, leading to a fail event (see § 7.11, page [*]), then exit the client program without destructing the Client object.

GlishEvent* NextEvent(
const struct timeval *tv = 0, int &timedout = dummy) waits for the next event to arrive and returns a pointer to a GlishEvent object representing it. This object will be Unref()'d on the next call to NextEvent(), so if you want to keep the GlishEvent pointer (or the Value pointer) you must Ref() it (or its value element).

You can use the tv parameter to pass in a pointer to a timeval structure that indicates how long NextEvent() should wait for an event before giving up. If the call does time out, the timedout parameter is set to a non-zero value. These parameters have default values and you do not need to worry about them.

If the connection to the interpreter is broken then NextEvent() returns a nil pointer and the caller should delete the Client object and terminate.

void Unrecognized()
must be called by any caller of NextEvent() if the current event is unrecognized (its name does not match one that the caller knows how to respond to).

void PostEvent( const GlishEvent* event, const EventContext &ctx = default )
sends out an event as represented by the given GlishEvent object. In the typical case, the default event context is used.

void PostEvent(
const char* name, const Value* value, const EventContext &ctx = default ) is similar to the preceding version of PostEvent(); it sends out an event with the given name and value. value can be nil if you know the event value won't be used. In the typical case, the default event context is used.

Typically this version of PostEvent() is more commonly used than the preceding version.

void PostEvent(
const char* name, const char* value, const EventContext &ctx = default ) sends the character string value as the event value. This function is often useful for sending simple result values to the interpreter.

void PostEvent(
const char* name, const char* fmt, const char* arg, const EventContext &ctx = default ) sends out a string-valued event with the given name, using a printf-style format and an associated string argument to construct the value of the event. For example,
 client->PostEvent( "error", "couldn't open %s", file_name );
sends an error event identifying which file could not be opened. In the typical case, the default event context is used.

void PostEvent(
const char* name, const char* fmt, const char* arg1, const char* arg2, const EventContext &ctx = default ) is quite similar, but providing two string arguments for the formatting instead of just one.

void Reply( Value* value )
replies to the most recently-received request/reply event (see § 7.4, page [*]) with the given value. Note that presently each request/reply event must be paired with a corresponding Reply call, with no other events sent or received during the interim.

int ReplyPending() const
returns true if the client has a received a so-far-unanswered request/reply event, false otherwise.

void Error( const char* msg )
posts an error event whose value is the given string. It is intended that such an event constitute a valid reply to the most recently-received request/reply event (see § 7.4, page [*]), but at the moment the receipt of the error event instead of a reply generates a warning from the interpreter.

void Error( const char* fmt, const char *arg )
posts an error event using the format string and the arg to create the error value.

int HasInterpreterConnection()
returns true if the client was invoked by a Glish interpreter, false if the client is running stand-alone (i.e., reading string-valued events from stdin and sending string representations of outbound events to stdout, as described in § 15.5.1, page [*]).

int HasEventSource()
returns true if the client has some sort of event source, either a connection to the Glish interpreter, or running stand-alone and reading events from stdin (§ 15.5.1, page [*]), and false if there is no event source whatsoever (due to use of -noglish. (See § 16.4, page [*].)


Multiplexing Input Sources

Some Glish clients need to receive input from multiple sources, such as both user-interface input and event input. The Client class provides three additional member functions to support input multiplexing. The basic idea is that the Client class makes available an fd_set identifying which file descriptors it uses to receive events. This fd_set is then used in a call to select() to determine whether any of the client's event sources are active. Another Client member function takes the fd_set returned by select() and reports whether or not the modified fd_set indicates that an event is pending. If so then a special version of NextEvent() is called with the fd_set passed as an argument; it decodes the fd_set and returns the pending event.

The additional member functions are:

int AddInputMask( fd_set* mask )
adds to the fd_set mask any file descriptors used by the Client object to receive events. It returns the number of file descriptors added (i.e., that weren't previously set but now are).

Note that the collection of file descriptors in general changes dynamically, meaning that AddInputMask must be called prior to each call to select() (or at least after every call to NextEvent). Alternatively, you can override the FD_Change virtual member function (see below) to get explicit notification of changes.

int HasClientInput( fd_set* mask )
returns true if the mask indicates that an event is pending for the Client.

GlishEvent *NextEvent( fd_set *mask )
returns the next event from the file descriptor indicated by mask. It is assumed that one of these file descriptors has information waiting to be read. If not, an error is posted.

virtual void
FD_Change( int fd, int add_flag ) is a virtual function that is called automatically whenever the Client's input sources change (due to newly-executed link or unlink statements). If add_flag is true then the given fd is a new input source; if false, then it is no longer an input source.

The default version of this member function does nothing, so you needn't call it if you override the function in a subclass.

Putting these member functions together, suppose you have an fd_set called mask which already has set in it the non-Glish file descriptors you use for input. The following fragment illustrates how you can multiplex between these input sources and the Glish sources:

    // Assume c is a pointer to our Client object.
    // Add c's input sources into the mask.
    c->AddInputMask( &mask );

    // Now select between the different sources.
    if ( select( FD_SETSIZE, &mask, 0, 0, 0 ) < 0 )
        error();
    else
        {
        if ( c->HasClientInput( &mask ) )
            {
            GlishEvent* e = c->NextEvent( &mask );
            handle_event( e );
            }

        // Check our other input sources for activity, too.
        ...
        }


Shared Clients

The most recent change to clients is the ability for clients to be shared among multiple interpreters. For this to work, the glishd must be running as root on the machines where shared clients will be running. (See § 16.2.1.) These shared clients allow multiple Glish interpreters to connect to the same Client object. The type of users allowed to connect is controlled by a parameter to the Client constructor. (See § 15.5.1.) Each of the PostEvent() functions take an optional parameter that specifies to which context the event should be sent. And the EventSources() function provides access to all of the sources a given client is managing.

The shared client member functions are:

GlishEvent* NextEvent( EventSource* source )
returns the next event from a particular event source. Because a single Client can be shared, it can have more than one event source, represented by the class EventSource. All of the EventSources managed by this Client class are accessed with the EventSources() member function.

ShareType Shared()
returns the share type of the Client, either Client::NONSHARED (the typical case), Client::USER, Client::GROUP, or Client::WORLD.

const EventContext &LastContext()
returns the event context for the most recently received event.

source_list &EventSources( )
returns the list of all of the event sources managed by this client.

Below is an example of how these functions work. This client forwards events which it receives to all of the other sources that are connected to it. It is like a multi-way tee connecting its event sources.

    int main( int argc, char** argv )
        {
        Client c( argc, argv, Client::GROUP );

        for ( GlishEvent* e; (e = c.NextEvent()); )
            {
            source_list &sources = c.EventSources();
            loop_over_list( sources, i )
                if ( sources[i]->Context() != c.LastContext() )
                    c.PostEvent( e, sources[i]->Context() );
            }
        return 0;
        }
The Client c, in this example, only accepts connections from other users in the same (UNIX) group as the user group who starts this client.


next up previous contents index
Next: The Proxy and ProxyStore Classes Up: The Glish Client Library Previous: An Example of a Client   Contents   Index
Please send questions or comments about AIPS++ to aips2-request@nrao.edu.
Copyright © 1995-2000 Associated Universities Inc., Washington, D.C.

Return to AIPS++ Home Page
2006-10-15