The application


1.1 Introduction

Object Oriented means that also the application is associated with an object.  This application object is a singleton: there can be only one such an object at any given time.  The purpose of the application object is to encapsulate variables that need to be available application wide.  It takes care of the initialization of libcw and the processing of command line parameters.  Also signal handlers are initialized by the application object.

1.2 The application object

1.2.1 Base class

The application class must be derived from the template libcw_app_tct<class UserApp>, where UserApp is the application class itself.

#include <libcw/libcw_app.h>

class MyApplication : public libcw_app_tct<MyApplication> {
  friend class Instance;
  ...
};

The template class libcw_app_tct is a Singleton and thus follows the general rules for Singleton objects: Its constructors must be private and friend class Instance; must be added.  The static instance is to be defined in a source file:

namespace {
  // reserve static space for the application object:
  SingletonInstance<MyApplication> app_instance __attribute__ ((unused));
}

It is possible to derive a custom application base class to provide standard command line options and/or default signal behavior.  Also this base class needs to follow the rules for Singleton objects:

#include <libcw/libcw_app.h>

template<class FINAL, class CHILD = FINAL>
  class MyApplicationBase : libcw_app_tct<FINAL> {
    friend CHILD;
    ...
  };

1.2.2 Constructor

The constructor of the application class can only have a default constructor.

The constructor is the place to tell libcw which command line parameters the application understands.  It should contain a list of add_option() calls and, if any command line arguments (that are not an option or option argument itself) need to be processed, a call to add_options_done().  See §1.3 for a description of those two member functions.

For example,

Header file:

class UserApp : public libcw_app_tct<UserApp> {
  friend class Instance;
  ...
private:
  UserApp(void);
};

Source file:

UserApp::UserApp(void)
{
  // List of command line options
  add_option( ... );
  add_option( ... );
  ...
  // Process remaining arguments
  add_options_done( ... );
}

Be sure to make the constructor private.

1.2.3 Accessing the application object

Libcw provides a global point of access to the application object; instead of using global variables, declare private variables inside the application class and add public methods to interact with them.  Variables representing command line parameters should be attributes of the application class.

The application object can be accessed from anywhere with the usual static method instance().

For example,

  if (MyApplication::instance().is_daemon())
    ...

1.2.4 Starting an application:  main()

An application that is linked with libcw should not contain a main() function.

See also §1.4 Initializing and starting the application.

1.2.5 Exiting an application

In order to exit an application call the member function libcw_app_ct<UserApp>::libcw_exit(exit_code), where exit_code is the return value of the application (Ie. UserApp::instance()->libcw_exit(0)).  This method flushes the debug file (if any, see --log-file below) and frees the memory allocated by libcw, before exiting.

1.3 Command line parameters

1.3.1 Default command line options

The base class libcw_app_tct provides five default command line options:

--help : Call the virtual member function print_usage(cout) and exit successfully.
--version : Call the virtual member function print_version(cout) and exit successfully.
--libcw-version : Print libcw version information to cout and exit successfully.
--foreground, -f : Run the application in the foreground.
--daemon : Run the application in the background.

When the macro DEBUG is defined, four more command line options are provided and the application runs by default in the foreground, otherwise the application runs by default in the background.  The four extra debug options are:

--log-file debug-file-l debug-file : Log debug output to debug-file.
--debug : Turn on all debugging (same as --debug-enable all).
--debug-enable debug-channels-d debug-channels : Turn on debug-channels, a comma seperated list of debug channel names.
--debug-disable debug-channels-u debug-channels : Turn off debug-channels.

Here debug-channels is a comma seperated list of debug channels.  When the application is started, a list of all debug channel names is automatically printed.  This list also reflects whether the output to each debug channel was enabled or disabled.  Channel names can be abbreviated.  If the abbreviation is not unique, the first matching channel name is chosen.  The matching of the channel names is not case sensitive.  There is one special debug channel that can be used to refer to all debug channels at once: ALL.

For example,

to turn on all debug channels except LLISTS and MALLOC, one can type:

$ foobar --debug-enable All --debug-disable llists --debug-disable MAL

while the shortest way to achieve the same is

$ foobar -da -ul,m

1.3.2 Printing version information

virtual void libcw_app_ct::print_version(ostream& os) const = 0;

This virtual function is called when the command line option --version is given.  It does not have a default but must be defined in the final application class and print the version and copyright information to the given ostream.

1.3.3 Printing a usage message

virtual void libcw_app_ct::print_usage(ostream& os) const;

This virtual function is called when the command line option --help is given.  It is also called from the default finish_options() when errflg is set.  The default behavior of this function is to print the usage message with a layout as shown in this example:

~/c++/libcw/src/io/tests>burst_tst --help
Usage:
    burst_tst [options]
Options:
    --backlog <number>                 : Backlog of the listen socket.
    --number-of-clients <number>,
    -n <number>                        : The number of clients to connect.
    --help                             : Print this help and exit.
    --version, -V                      : Print version information and exit.
    --libcw-version                    : Return version of linked libcw.
    --daemon                           : Run program in the background.
    --log-file <debug-file>,
    -l <debug-file>                    : Log debug output to <debug-file>.
    --debug                            : Turn on all debugging (same as -d all).
    --debug-enable <debug-channels>,
    -d <debug-channels>                : Turn on <debug-channels>, a comma
                                         seperated list of debug channel names.
    --debug-disable <debug-channels>,
    -u <debug-channels>                : Turn off <debug-channels>.

where everything below the line «Options:» is printed by calling

void libcw_app_ct::print_options(ostream& os, char const* indent) const

This method prints a list of all options and their description to the given ostream.  Each option that was added with add_option() is listed, unless the description passed to add_option() was a NULL pointer.

1.3.4 Adding command line options

Command line options are added in the constructor by calling the member function

template <class UserApp>
void libcw_app_ct::add_option(void (UserApp::*callback)(char const*),
                              char const* long_name,
			      char        short_name,
			      char const* usage_argument_name,
			      char const* usage_description);

where callback must be a member function processing a single command line option with the long name long_name and/or the short name short_name.  When only a short option is desired then long_name should be NULL and when only a long option is desired then short_name should be 0.  The parameter usage_argument_name determines if this option takes an argument: the option does not take an argument when this parameter is NULL.  Otherwise usage_argument_name points to a string printed in the usage message at the place of the argument.  If the first character of usage_argument_name is a '[' then the argument is optional, otherwise the argument is required.  Finally, usage_description should point to a string describing the option, or when usage_description is NULL then no usage message is printed for this option at all.

Options defined in the base class of the application object can be overridden by adding an option with the same name.  For instance, in the example below the default -V option that normally means the same as --version, is overridden to mean Verbose.

For example,

UserApp::UserApp(void) : verbose(false)
{
  add_option(&UserApp::command_line_option_verbose,
      NULL, 'V', NULL, "Make application Verbose.");
  add_option(&UserApp::command_line_option_number_of_clients,
      "number-of-clients", 'n', "<number>", "The number of clients to connect.");
  ...
}

void UserApp::command_line_option_verbose(char const* UNUSED(opt))
{
  verbose = true;
}

void UserApp::command_line_option_number_of_clients(char const* opt) // `opt' is "-n" or
{								     // "--number-of-clients"
  number_of_clients = atoi(optarg);
  if (number_of_clients <= 0 || number_of_clients > 1020)
  {
    cerr << application_name << ": " << opt << " : out of range" << endl;
    libcw_exit(-1);
  }
}

would cause the usage message to become

Usage:
    progname [options]
Options:
    --number-of-clients <number>,
    -n <number>                        : The number of clients to connect.
    -V                                 : Make application Verbose.
    ...
    --version                          : Print version information and exit.

1.3.5 Command line arguments

Extra command line arguments can be processed by adding the following member function to the constructor of the application class.

template <class UserApp>
void libcw_app_ct::add_options_done(void (UserApp::*options_done)(int&, char* const*&),
                                    char const* usage_arguments);

After all command line options are decoded, the application calls the list of options_done member functions that were passed as argument of add_options_done(), starting with the one that was added last (in the constructor of the final application) and ending with the one that was added first (in the constructor of the base class).  The options_done call back functions must be of the form

void UserApp::myOptionsDone(int& argc, char* const*& argv)

where argc is the number of arguments in the array argv.  The pointer argv points to the start of an array of pointers to the remaining command line arguments (those which are not already processed as option or option argument).  If this member function processes any command line argument, it should update argc and argv otherwise the base class of the application will process that argument again.

For example,

UserApp::UserApp(void)
{
  ...
  add_options_done(&UserApp::get_filename, "[filename]");
}

void UserApp::get_filename(int& argc, char* const*& argv)
{
  if (argc != 1)
    errflg = true;
  else
    filename = argv[0];
  --argc;
  ++argv;
}

would cause the usage message to become

Usage:
    progname [options] [filename]
Options:
    ...

1.3.6 Error handling

When an error is detected during the processing of command line arguments then the boolean errflg should be set.  It is also set when an unknown command line option was given or a mandatory option argument was missing.

After all options and arguments are processed, libcw calls the virtual member function

virtual void libcw_app_ct::finish_options(void) const

This is the prefered function from which print_usage(cerr) should be called in case of an error.  It can also be used to check for inconsistent combinations of command line options; although it makes more sense to include option dependent code in an options_done member function, as this method can be overridden by a derived class while an options_done can not.  The default implementation of finish_options is

void libcw_app_ct::finish_options(void) const
{
  if (errflg)
  {
    print_usage(cerr);
    libcw_exit(2);
  }
}

1.4 Initializing and starting the application

After initializing itself and processing all command line arguments, libcw calls the virtual function start().  Here the application can be initialized and the main loop entered by calling mainloop()

For example,

class MyApplication : public libcw_app_tct<MyApplication> {
  ...
protected:
  virtual void start(void);
  ...
};

void MyApplication::start(void)
{
  // Initialisation:
  ...
  // Start main loop:
  libcw_exit(mainloop());
}

The reason that mainloop() has to be called from start() is to allow the user to execute more code after it returns.  It is possible to call mainloop() more then once.


Copyright © 1999 Carlo Wood.  All rights reserved.