next up previous contents
Next: Required and Generated Up: A USERS GUIDE Previous: Dynamic Configuration/ User

Writing a Module

One advantage of working in C++ is that part of the work of writing a module is done for the user by inheriting behavior from a Module parent class (AppModule) provided by the framework package. Below is a selection from the AppModule class declaration. The full declaration is not included because it contains many unimportant details:

class AppModule : public APPExecutable { // AppModule inherits from APPExecutable
//------------------
// Static Members --
//------------------
protected:

    static AbsEvent* _theAbsEvent;       // a pointer to the event

//--------------------
// Instance Members --
//--------------------

public:

    // Constructors
    AppModule( const char* const theName, const char* const theDescription );

    // Destructor
    virtual ~AppModule( );

    // Operations    
    virtual void           begin( AppJob* aJob );
    virtual void           begin( AppRun* aRun );
    virtual AbsEvent*      event( AbsEvent* anEvent, int mode = 0 );
    virtual void           other( AbsEvent* anOther );
    virtual void           end  ( AppRun* aRun );
    virtual void           end  ( AppJob* aJob );
    virtual void           abort( AppJob* aJob );
    virtual void           talkTo( );
    virtual void           help( );
    virtual void           exit( );
...
};
Notes:
  1. Each of the above operations represent processing steps that will be called by AC++ when the appropriate state is reached.
  2. The constructor for the module takes a name that will be used to identify the created module-instance (see section on module-instances) in any user interface command. The ``theDescription'' argument is for informational purposes only.
  3. Function overloading differentiates the begin( ) and end( ) functions.

Here is an example user module class definition:

class ParmExample : public AppModule {  // ParmExample inherits from AppModule

//--------------------
// Instance Members --
//--------------------

public:

    // Constructors
    ParmExample( const char* const theName, const char* const theDescription );

    // Destructor
    virtual ~ParmExample( );

    // Operations
    virtual void           begin( AppJob* aJob );
    virtual AbsEvent*      event( AbsEvent* anEvent, int mode = 0 );

protected:

  // example parameter variables which adjust the behavior of the module
  AbsParmGeneral<double> _parm1, _parm2;
};
Notes:
  1. _parm1 and _parm2 are the member data of this module class. Because they are of type AbsParmGeneral<T> they are set-able via a talk_to. Member data of type AbsParmGeneral<T> are analogous to parameter sets in ANALYSIS_CONTROL. However any data item that is needed by more then one operation of the module should go here even if it is not meant to be a member of a parameter set or be setable by a talk to.
  2. The user module inherits from AppModule. This means that many of the module operations are actually implemented in the parent class. Only the entries that stpana needs to specialize are declared here. The most significant default behavior is the talk_to method. This no longer needs to be written by the user module since the only behavior ParmExample needs is that the talk_to be able to set the _parm1, and _parm2 variables. The ability to set class data variables that are declared to the framework package is the one thing that AppModule:talk_to method knows how to do. Besides the obvious advantage that users no longer write talk-to's the other advantage is that uniformity in the end-user interface is enforced. No longer will the way to set variables vary from module to module. The equivalent of .uic files (which are now .tcl files) will look naturally become more uniform.
  3. The event member function, takes a pointer to the AbsEvent as a passed argument and returns an optionally modified version of it. This is the way that modules access event data. The fact that the event is passed by pointer means that the framework code itself needs to know very little about the event data structure. This is what allows CDF and BaBar to share this code. It also means that if we change our minds about what an event is, the framework code will need minimal modification.
  4. The second argument to the event operation, ``mode'' is not used at CDF.

Here is the implementation file for the above example:

...
//----------------
// Constructors --
//----------------

ParmExample::ParmExample( 
    const char* const theName, 
    const char* const theDescription )
  : AppModule( theName, theDescription ), // Pass args. to parent class
    _parm1("parm1", this, 1),             // Initialize parm1 to 1
    _parm2("parm2", this, 2)              // Initialize parm2 to 2
{
  // Add parameters to list of command handlers
  commands( )->append( &_parm1 );
  commands( )->append( &_parm2 );
}
//--------------
// Destructor --
//--------------

ParmExample::~ParmExample( )
{
}
//--------------
// Operations --
//--------------

void
ParmExample::begin( AppJob* )
{
    cout << name( ) << " begin Job" << endl;
    printAllAbsParm(cout);   // formated print out of default values
}
AbsEvent*
ParmExample::event( AbsEvent* anEvent, int )
{
  cout << name() << " parm values: "<< _parm1.value() << " " << _parm2.value() << endl;
  return (AbsEvent*)anEvent;
}
Notes:
  1. This example uses the C++ initializer syntax. The name of the constructor and its arguments is followed by a ``:'' and then a list of things to initialize including the parent class. Here the parent class is being initialized with the arguments passed to the child class constructor. Then the member data of the ParmExample class is being initialized by calling the three argument constructor of AbsParmGeneral<double> (which is the type of _parm1, _parm2). The first argument is a string that will be used as the command name for changing the parameter in a talk-to. The second argument is a pointer to the module that this command/parameter belongs to. (In C++ ``this'' is a compiler defined pointer to the current class object, in this case it refers to the instance of ParmExample that is being constructed.) The third argument is the default value the parameter should have.
  2. In the begin job operation a friend function of AbsParm is called in order to print the default values of the module instance.
  3. In the event operation the values are again echo'ed. At this point they may have been changed via a talk-to, with a command like this:
    module talk ParmExample2; parm2 set 1111; exit
    




next up previous contents
Next: Required and Generated Up: A USERS GUIDE Previous: Dynamic Configuration/ User



Liz Sexton
Fri May 16 16:37:56 CDT 1997