nscl_logo_small.gif (2463 bytes)

Adding Tcl Commands to SpecTcl

HH00706_.wmf (6530 bytes)

SpecTcl Home  General Information User Guide Programmer's Guide Obtaining and Installing

Commands can be added to SpecTcl as follows:

checkbox51.gif (229 bytes) Write either a single TCL Command class or a package of Tcl commands.
checkbox51.gif (229 bytes) Modify AppInit.cpp to register the package or single Tcl command with the interpreter.
checkbox51.gif (229 bytes) Modify the SpecTcl Makefile if necessary to include the command or package of commands.
checkbox51.gif (229 bytes) Build the modified SpecTcl.

Writing Tcl Command class.

SpecTcl includes an object oriented encapsulation of Tcl/Tk.  This encapsulation makes it easy to add Tcl Command processors as objects which are members of a particular class.  You create a Tcl Command class by deriving a class from CTCLProcessor.   CTCLProcessor is defined in the file <TCLProcessor.h> .  The key member functions of this class which you will need to understand are:

Name/Type Arguments Description
CTCLProcessor (const std::string& sCommand,
CTCLInterpreter* pInterp)
Constructor.  sCommand is the name of the command, and pInterp is a pointer to the TCL interpreter object (see <TCLInterpreter.h>) for a description of that class.
CTCLProcessor (const char* pCommand,
CTCLInterpreter* pInterp)
Alternate form of the constructor where the command name is passed in as a null terminated string rather than an std::string.
virtual int operator() (CTCLInterpreter& rInterpreter,
  CTCLResult&      rResult,
  int       nArguments,
  char*  pArguments[])
Function called by the Tcl/Tk interpreter to execute the command.   rInterpreter referes to the interpreter object.  rResult to the interpreter result string, the result string can be thought of as a return value from the command.   CTCLResult is defined in <TCLResult.h>.  The last pair of parameters are the number of command line parameters (including the command name)(nArguments), and an array of pointers to the comand line paramters.  You must implement this function to provide the functionality for your command, You should return either TCL_OK if the command executed correctly or TCL_ERROR if the command detected an error.
virtual void OnDelete () Called as the command is being removed from the interpreter.  You do not need to ovderride and implement this function unless you have some cleanup which must be done at that time, rather than the destructor.
void Register () Registers the command on the interpreter to which it is bound.
void Unregister () Unregisters the command from the interpreter to which it is bound.   Note that this will cause OnDelete() to eventually be called.

To implement your command you will usually need to at least write a constructor and override the base class operator() function.  A minimal constructor might call the base class constructor passing the command name and the interpreter to it, for example for a command named "glorp"

CGlorpProcessor::CGlorpProcessor(CTCLInterpreter* pInterp) :
   CTCLProcessor("glorp", pInterp) {}

The operator() member is called when the command is executed by Tcl/Tk.  It is responsible for providing the actual command functionality.  Supose for example, that "glorp"'s function is to return as a result string the value of the nArguments.   You would the implement operator() as follows:

int
CGlorpProcessor::operator()(CTCLInterpreter& rInterp, CTCLResult& rResult,
                            int nArgs, char* pArgs[])
{
   char snArgs[100];
   sprintf(snArgs, "%d", nArgs);       // Produce string encoding of nArgs and
   rResult = snArgs;                   // copy it into the result string before
   return TCL_OK;                      // returning successfully.
}

 

Top

Writing a package of Tcl Commands.

If you have a set of commands which are functionally related, it may be advisable to create a Command package.  A command package is a class which contains objects derived from CTCLPackagedCommand (a set of related commands), which is a subclass of CTCLProcessor.  The package class, derived from CTCLCommandPackage supports gang registration and unregistration of commands it contains from the interpreter, as well as typically providing some shared services for these commands.  CTCLPacakgedCommand supplies a member giving access to the package which will allow you to call back into the packages services.

Key member functions of CTCLCommandPackage

CTCLCommandPackage is defined in the header file: <TCLCommandPackage.h>.  Below is a table of the most important functions in this class.  Typically you will derive from this class and add additional member functions to support the needs of the commands which are packaged.

Name/Type Signature Description
CTCLCommandPackage (CTCLIntepreter* pInterp,
const std::string& rSignon)
Constructor. pInterp points to the TCL interpreter object representing the Tcl Interpreter. rSignon is a reference to a string identifying the package. This string is stored in the package and can be fetched for display or whatever other use is desired.
CTCLCommandPackage (CTCLInterpreter* pInterp
const char* pSignon
Same as prior constructor, except the signon string is passed in as a pointer to a null terminated 'c' string.
void AddProcessor (CTCLProcessor* pProcessor) Adds a command processing object to the list of commands which are in the package.
void AddProcessors (CommandList& rList) Adds a list of command processing objects to the package manager.
void Register () Registers all of the command processors with the interpreter.
void Unregister () Unregisters all of the pakage's command processors from the interpreter.

Top

Key member functions of CPackagedCommand

The CPackagedCommand is a command processor intended to be indeted into a command package. It is a CTCLProcessor, however it has additional members to support directly membership in a command package.

Name/Type Arguments Description
CTCLPackagedCommand (const std::string& rCommand,
CTCLInterpreter* pInterp,
CTCLCommandPacakge& rPackage)
Constructor. The rCommand parameter is the command name (e.g. in the previous section std::string("glorp")). pInterp points to the Tcl Interpreter object and rPackage which contains this object.
CTCLPackagedCommand (const char* pCommand,
CTCLInterpreter* pInterp,
CTCLCommandPacakge& rPacakge)
Same as above, however the command name is a simple C null terminated string.
CTCLCommandPackage& getMyPackage () Retrieves a reference to the package. Typically this will have to be cast to the appropriate actual package type e.g.:
CMyPackage& rPack = 
          (CMyPackage&)getMyPackage()

Examples of a simple package and its commands are given in the following files:

RunControlPackage.h Header file defining a package, CStartStopPackage to control SpecTcl's analysis
RunControlPackage.cpp Implementation of nontrivial member functions of CStartStopPackage
StopRun.h Header file defining the CStartCommand class which implements the SpecTcl stop command.
StopRun.cpp Implementation of the SpecTcl stop command.

Top

Modifying AppInit.cpp to register the new commands.

When you copy the SpecTcl files from the Skel distribution directory, one of the files you get is AppInit.cpp. This file contains initialization code for SpecTcl. AppInit defines the class CTclGrammerApp, instantiates a static instance of this class and fills the global variable gpTCLApplication with a pointer to this instance.

The SpecTcl startup code creates the Tcl Interpreter, does a few other initializations common to all SpecTcl programs (e.g starts up the displayer and so on), and then invokes CTclGrammerApp's operator() member. This function can perform additional initializations prior to entering SpecTcl's main command loop.

CTclGrammerApp has several member variables and functions. Amongst them are pointers to all of the command packages which are used by SpecTcl (this allows you to create versions of SpecTcl with command packagess disabled as well as additional commands). Modifying AppInit.cpp to include a new package or command involves:

Adding a member variable to point to your package.
Updating CTclGrammerApp's constructor and destructor to initialize and delete this pointer.
Adding code to CTclGrammerApp::RegisterPackages() to instantiate and register your package.

The discussion below will assume you are registering a package of commands. The method for registering single commands is essentially identical.

Adding Members to CTclGrammerApp

A sample declaration of a package pointer is shown below:

CRunControlPackage* m_pRunControlPackage;

This should be placed in the class definition in this file.

Updating CTclGrammerApp's constructor and Destructor.

At construction time, the interpreter is not yet well defined. Therefore, it is not possible to instantiate the actual package or its commands at this time. Therefore the package pointer should be initialized to null. The code below shows this for m_pRuncontrolPackage:

 

CTclGrammerApp() :
m_nDisplaySize(0),
m_nParams(0),
m_nListSize(0),
m_pAnalyzer(0),
m_pHistogrammer(0),
m_pRunControl(0),
m_pRunControlPackage(0),
m_pParameterPackage(0),
m_pSpectrumPackage(0)
{
...

Adding code to register the package.

The member function RegisterPackages is responsible for instantiating and registering command packages. Below is sample code which initializes the Run Control package:

 

m_pRunControlPackage = new CRunControlPackage(getInterpreter());
m_pRunControlPackage->Register();
m_pRunControlPackage->InitializeRunState();
cerr << m_pRunControlPackage->getSignon().c_str() << endl;

A new Command package is created. The command package, on instantiation creates its list of packaged commands. The next statement then registers this list with Tcl. The package itself requires some further initialization and this is then done with a call to its member function InitializeRunState(). Finally, the package signon message is printed to stderr.

Top

Modifying the SpecTcl Makefile

When you copy the files from SpecTcl's Skel directory, one of the file you get is Makefile If, while adding the command or packages, you have created new program files, this Makefile should be modified prior t attempting to build SpecTcl. This involves the following steps:

Adding an object file to the link stage.
Adding a buld rule for the object.

Adding an object file to the link stage.

Locate the Makefile line which reads:

OBJECTS=AppInit.o UserCode.o

Add the name of your object files to the end of this line. Each file should be separated by spaces from the preceding file. If necessary, continuation lines can be used, by terminating the previous line with the backslash ("\") character.

Adding a build rule for your object file.

Add a Makefile build rule for each of your new objects. See the documentation on the Unix make(1) utililty for information about the make utility.

Top

SpecTcl Home  General Information User Guide Programmer's Guide Obtaining and Installing


Last Modified: October 28, 2003 by: fox@nscl.msu.edu
© Copyright NSCL 1999, All rights reserved