4 The Class OH323Tut

4.1 Declaration

Each program that is based on PWLib should have one instance of a class descended from PProcess or alternatively from PServiceProcess (which is itself a descendant of PProcess). In our tutorial application, this class is called OH323Tut. The class is declared in file oh323tut.h (lines 3443). The virtual method Main() (line 41) is the place where we put the code that would otherwise (in a C/C++ program that does not use PWLib) go into the program's main() function.

The declaration of OH323Tut is quite simple. Besides the method Main(), there are only a constructor, the destructor, the method printHelp() (declared in line 42) that prints information about the usage of the program, and the PCLASSINFO macro. The task of the macro (line 36) is to insert run-time typing methods that are mandatory for each member of the PWLib class hierarchy. If you are interested, the macro is defined in file pwlib/include/ptlib/object.h.

4.2 Implementation

The OH323Tut's constructor is in file oh323tut.cxx at line 58. The constructor's only task is to pass parameters to the parent class (PProcess) constructor. The parameters are, in this order, the author of the application, the name of the application, the application's major and minor version numbers, the code status (possible values are AlphaCode, BetaCode, and ReleaseCode), and build number. We set the last four parameters using constants that we define in file pconf.h (lines 5053).

A search of the oh323tut's sources would show you that none of the files explicitly contains a main() function. The main() is generated by the macro PCREATE_PROCESS (file oh323tut.cxx, line 56). On Unix platforms, the macro is defined as follows:

#define PCREATE_PROCESS(cls) \
  int main(int argc, char ** argv, char ** envp) \
    { PProcess::PreInitialise(argc, argv, envp); \
      static cls instance; \
      return instance._main(); \

The code inserted by the macro is a bit different on each platform supported by PWLib, but the Unix variant above should give you an idea about what is going on under the hood. The main() simply does some initializing, allocates an instance of the class cls (OH323Tut in our case) and calls the method  _main() of that instance. The  _main() in turn calls the virtual method Main(). The macro helps you to create simpler multi-platform programs — a one-liner is more elegant than several #ifdef's.

We continue with the description of OH323Tut's method Main() (file oh323tut.cxx, lines 92168). We skip lines 9497 (we will return to them later) and discuss the parsing of command-line arguments.

At line 98, we create an instance of class PConfigArgs:

PConfigArgs args(GetArguments());

This is a programming idiom used in many PWLib programs. GetArguments() is a method of PProcess that returns the list of arguments given to the program on the command line.

The subsequent command at line 100 tells the object args which command-line options we need to parse. Each option is given in its short and long form, both without the leading '-' sign. So for example, the substring "h-help." means that the request for help will appear either as -h or --help on the command line. The substrings that describe options with an option string (e.g. -f or --file has to be followed by the file name) end with a colon ":", e.g. "f-file:". The substrings that describe options without an option string end with a dot, e.g. "h-help.". Note that the arguments 't' and 'o' are only included if the program compiles with tracing enabled (which is the default for both the opt and debug targets).

The only mandatory command-line option for the oh323tut application is -f (or --file). Because of that, lines 116120 display the help and end the program if the option 'f' is missing or if 'h' ('-help') is present.

Lines 122126 set the level of tracing and optionally the tracing output file. Lines 128156 process the individual command-line options. We use an instance of class ProgConf (declared in file pconf.h, lines 3247) to store the information obtained from the command line.

The three options 'n', 'g', and 'G' relate to the registration of the endpoint with a gatekeeper. We first set progConf.gkMode to ProgConf::RegisterWithGatekeeper — this is the default behavior in case none of the three gatekeeper options is used (line 130). The order in which the options are processed is important (lines 132145). If the user gives the program both -n and -g/-G, then -n is overridden. Both 'g' and 'G' set progConf.gkMode to the value ProgConf::RegisterWithGatekeeper and they both require an option string which is stored in progConf.gkAddr or in progConf.gkId, respectively.

Line 147 stores the file name in progConf.fileName. Note that we do not need to test for the presence of the option 'f' — we have already done that at line 116. Line 149 tests for the presence of the option 'p'. If the option is present, the listen port number is assigned to progConf.port. Line 152 does a simple port number validity test — if the number is zero, we set it back to the default.

Line 155 tests for the presence of the option 'u' (user alias). The user can give several aliases on the command line and the PString returned by the expression args.GetOptionString(u'') will contain each alias on a separate line. The method Lines() transforms the string to an array of individual aliases (lines) and we assign the array to progConf.userAliases.

Line 159 creates an instance of class MyEndPoint. If the initialization of the endpoint (line 161) succeeds, the execution of the application's main thread is blocked at line 164. The object terminationSync is an instance of the class PSyncPoint (see line 36). PSyncPoint is in fact a semaphore that is initially locked, so the thread that calls the method Wait() gets blocked until another thread calls the method Signal().

We want to stop the application when the user hits Ctrl-C. To achieve that, we use the object terminationSync together with Unix signal handling. The signal handler is defined at lines 3851 and we register it for SIGINIT and SIGTERM at lines 9497. When signalHandler() receives either of the two signals, it simply calls terminationSync.Signal(). This unblocks the thread of execution that waited at line 164. When the method Main() ends, all automatic variables created by the method are deallocated. This includes the object endpoint — its destructor closes all active connections (if there are any), and does all the necessary endpoint shutdown steps. We describe the endpoint class in the next section.


Next: 5 H.323 Endpoint