int PROGRAM = -1;A limitation of this approach is that you are required to provide code to set the global variables appropriately. Also, due to the variables using reverse logic, if you wanted a particular message to be output if either of two variables is enabled, non intuitive arithmetic is required. For example, even if we ignored the current trace level and only enabled output if a variable was set to zero, we would still have to write:
int LIBRARY = -1;
main()
{
// ... set global variables
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM) << "PROGRAM" << endl;
OTCLIB_TRACER(LIBRARY) << "LIBRARY" << endl;
return 0;
}
OTCLIB_TRACER(!(!PROGRAM || !LIBRARY))To make the task of performing arithmetic on these variables easier, their functionality is incorporated into the OTC_TraceSwitch class. This class hides the fact that reverse logic is required and allows you to use standard logical expressions. For example:
<< "PROGRAM || LIBRARY" << endl;
OTC_TraceSwitch PROGRAM;When created, trace switches will be disabled unless explicitly initialised. To enable or disable trace switches once created, you can assign to them a boolean value. A value of OTCLIB_TRUE will enable a trace switch, a value of OTCLIB_FALSE will disable a trace switch. One way of enabling the trace switches would be through command line options. For example:
OTC_TraceSwitch LIBRARY;
main()
{
// ... set value of global switches
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM) << "PROGRAM" << endl;
OTCLIB_TRACER(LIBRARY) << "LIBRARY" << endl;
OTCLIB_TRACER(PROGRAM || LIBRARY)
<< "PROGRAM || LIBRARY" << endl;
OTCLIB_TRACER(LIBRARY && !PROGRAM)
<< "LIBRARY && !PROGRAM" << endl;
return 0;
}
OTC_TraceSwitch PROGRAM;Since instances of the OTC_TraceSwitch class are lightweight, it is easier to leave them in code when other trace code is not being compiled into your program. If however, you do want them removed when other trace code is not being included, macros are provided for creating instances of the OTC_TraceSwitch class, as well as disabling them and enabling them, which when used will only result in the code being included into your program when the preprocessor symbol OTCLIB_TRACE is defined.
OTC_TraceSwitch LIBRARY;
main(int arcg, char* argv[])
{
OUX_Program::initialise(argc,argv);
while (OUX_Program::numOptions() != 0)
{
if (OUX_Program::option(1) == "-trace")
{
OUX_Program::shift(1);
if (OUX_Program::numOptions() == 0)
{
usage();
return 1;
}
else
{
if (OUX_Program::option(1) == "PROGRAM")
PROGRAM = OTCLIB_TRUE;
else if (OUX_Program::option(1) == "LIBRARY")
LIBRARY = OTCLIB_TRUE;
OUX_Program::shift(1);
}
}
else if (OUX_Program::option(1) == "-traceall")
{
OTC_Tracer::enableGlobalTrace();
OUX_Program::shift(1);
}
else
{
// ...
OUX_Program::shift(1);
}
}
OUX_Program::restore();
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM) << "PROGRAM" << endl;
OTCLIB_TRACER(LIBRARY) << "LIBRARY" << endl;
OTCLIB_TRACER(PROGRAM || LIBRARY)
<< "PROGRAM || LIBRARY" << endl;
OTCLIB_TRACER(LIBRARY && !PROGRAM)
<< "LIBRARY && !PROGRAM" << endl;
return 0;
}
The macro for creating an instance of the OTC_TraceSwitch class is OTCLIB_TRACESWITCH(). This macro accepts two arguments. The first argument is the name of the trace switch and the second argument is the initial value to be assigned to the trace switch. The initial value can be a boolean value, or a logical expression formed from other trace switches. For example:
OTCLIB_TRACESWITCH(PROGRAM,OTCLIB_FALSE);As the OTCLIB_TRACESWITCH() macro initialises the instance of the OTC_TraceSwitch class, as well as providing the declaration, it can only be used at either function or global scope. If you wish to have an instance of the OTC_TraceSwitch class as a member variable of a class, you will need to use the OTC_TraceSwitch class explicitly and initialise it in the member initialiser list of the class' constructor.
OTCLIB_TRACESWITCH(LIBRARY,OTCLIB_FALSE);
OTCLIB_TRACESWITCH(HERE,PROGRAM||LIBRARY);
To set the value of a trace switch, you can use the OTCLIB_SETTRACESWITCH() macro. This accepts two arguments. The first argument is the name of the trace switch. The second argument is either a boolean value, or a logical expression formed from other trace switches, which the trace switch should be set to. Alternatively, you could use the macros OTCLIB_ENABLETRACESWITCH() and OTCLIB_DISABLETRACESWITCH(). These macros accept a single argument of the name of the trace switch. The macros will enable and disable the trace switch respectively. For example:
// ... enable trace switch
OTCLIB_SETTRACESWITCH(PROGRAM,OTCLIB_TRUE);
OTCLIB_ENABLETRACESWITCH(PROGRAM);
// ... disable trace switch
OTCLIB_SETTRACESWITCH(LIBRARY,OTCLIB_FALSE);
OTCLIB_DISABLETRACESWITCH(LIBRARY);
In addition to being able to be enabled and disabled, instances of the OTC_TraceTag class can have a trace level associated with them. Whether trace information is displayed or not, is determined by using comparison operators in conjunction with the instance of the trace tag. For example, differing levels of verbosity of trace information would be realised as follows:
OTC_TraceTag PROGRAM("PROGRAM");To enable a tag, the environment variable OTCLIB_TRACETAGS should be defined before you run your program, to be a list of the tag names to enable. The name you use should correspond to the name given to the constructor of the OTC_TraceTag class. When you enable a trace tag, you may optionally assign a value to it to represent the trace level. If you do not assign the tag a trace level, it will be given a trace level of `0'. For example, if the OTCLIB_TRACETAGS environment variable was set to:
main()
{
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM)
<< "trace level 0 or higher" << endl;
OTCLIB_TRACER(PROGRAM >= 1)
<< "trace level 1 or higher" << endl;
OTCLIB_TRACER(PROGRAM >= 2)
<< "trace level 2 or higher" << endl;
return 0;
}
OTCLIB_TRACETAGS="PROGRAM"the following would be output for the above example:
@enter - main()If however the OTCLIB_TRACETAGS environment variable was set to:
trace level 0 or higher
@exit - main()
OTCLIB_TRACETAGS="PROGRAM=1"the following would be output for the above example:
@enter - main()The full range of comparison operators may be used with the OTC_TraceTag class, as well as the logical operators implemented by the OTC_TraceSwitch base class. This allows for quite complicated expressions to be used to determine if trace information should be displayed. For example:
trace level 0 or higher
trace level 1 or higher
@exit - main()
OTC_TraceTag PROGRAM("PROGRAM");As well as enabling tags through the environment variable OTCLIB_TRACETAGS, you can enable and set the value of trace tags from within your program. The function you should call to set the value of a trace tag is OTC_TraceTag::set(). This function takes two arguments. The first argument should be the name of the tag. The second argument should be the trace level for the tag. If you want to disable a trace tag, a value of `-1' should be passed as the second argument.
OTC_TraceSwitch LIBRARY;
main()
{
// ... set global trace switches
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM >= 0 || LIBRARY)
<< "PROGRAM || LIBRARY" << endl;
OTCLIB_TRACER(PROGRAM == 1)
<< "trace level 1" << endl;
OTCLIB_TRACER(PROGRAM >= 1)
<< "trace level 1 or higher" << endl;
OTCLIB_TRACER(PROGRAM >= 2)
<< "trace level 2 or higher" << endl;
OTCLIB_TRACER(PROGRAM > 1 && PROGRAM < 5)
<< "trace level between 1 and 5" << endl;
return 0; }
In addition to being able to set the value of a trace tag you know the name of, you can find out the list of trace tags the system currently knows about. In conjunction with the ability to set the value of a trace tag, this can be used to set trace tags through a mechanism other than the OTCLIB_TRACETAGS environment variable. For example, the following disables all trace tags currently active, and then sets the values of certain trace tags from a configuration database.
OTC_Map<OTC_String,int> configuration;Similarly to the OTC_TraceSwitch class, macros are provided which allow you to create instances of the OTC_TraceTag class and set the values of active tags. When using these macros, the code will only be compiled into your application when the preprocessor symbol OTCLIB_TRACE is defined.
// ...
// Disable all the tags first.
OTC_TagInfo* theInfo = OTC_TraceTag::tags();
while (theInfo != 0)
{
theInfo->setValue(-1);
theInfo = theInfo->next();
}
// Now set the values of the tags. The set() function
// automatically creates a new info objects for a tag
// which doesn't exist.
OTC_PairIterator<OTC_String,int> iter = 0;
iter = configuration.keys();
for (iter.reset(); iter.isValid(); iter.next())
OTC_TraceTag::set(iter.key(),iter.item());
The name of the macro used to create an instance of OTC_TraceTag class is OTCLIB_TRACETAG. This accepts a single argument to be used as the name of the instance of the class. The name is also converted into a string and passed to the constructor of the class for use as the trace tag name. If it is required that the instance of the OTC_TraceTag class have static extent, a variant of the above macro, called OTCLIB_STATIC_TRACETAG(), can be used instead. Both macros can only be used at global or function scope. If you wish to have a instance of the OTC_TraceTag class as a member variable of a class, you will need to use the OTC_TraceTag class explicitly.
A second macro, OTCLIB_SETTRACETAG(), is also provided. This takes two arguments and is equivalent to calling the function OTC_TraceTag::set(). The first argument to the macro should be the name of trace tag. The name should not be quoted. The second argument to the macro should be the value to which the trace tag is being set. For example:
OTCLIB_TRACETAG(PROGRAM);
main()
{
OTCLIB_SETTRACETAG(PROGRAM,0);
OTCLIB_MARKBLOCK(PROGRAM,"main()");
OTCLIB_TRACER(PROGRAM)
<< "trace level 0 or higher" << endl;
OTCLIB_TRACER(PROGRAM >= 1)
<< "trace level 1 or higher" << endl;
OTCLIB_TRACER(PROGRAM >= 2)
<< "trace level 2 or higher" << endl;
return 0;
}
Using a pattern to enable trace output, can be useful where you wish to enable tracing in all code files contained in a particular source directory. This can be achieved by using a string generated by SCCS or RCS source code control systems, to initialise an instance of the OTC_TracePattern class in each object file. For example, the following would be appropriate if RCS were being used. Note that the instance of the OTC_TracePattern class is made static so that its name is local to that object file.
static OTC_TracePattern MODULE =If it was now required to trace all code contained in the examples directory, the environment variable OTCLIB_TRACEPATTERN would be defined as follows:
"$Source: /home/ose/examples/_tracepat.cc $";
void function()
{
OTCLIB_TRACER(MODULE) << "this module" << endl;
// ...
}
OTCLIB_TRACEPATTERN="Source: .*/examples/.*"Alternatively, the trace pattern could be set from within your program by calling the function OTC_TracePattern::set(). This function takes a single argument consisting of the pattern which should be used to match against all pattern tags. Calling the function will cause all active pattern tags to be re-evaluated immediately.
As with the previous classes, macros are provided such that instances of the class, and code setting the trace pattern, will only be compiled into your program when the preprocessor symbol OTCLIB_TRACE is defined. The two macros available for creating instances of the OTC_TracePattern class are OTCLIB_TRACEPATTERN() and OTCLIB_STATIC_TRACEPATTERN(). These both take two arguments. The first argument is the name to use for the instance of the class, and the second argument is the text string to match against the trace pattern. The instance of the OTC_TracePattern class created using the OTCLIB_STATIC_TRACEPATTERN() macro will have static extent.
To change the trace pattern from within your program, the OTCLIB_SETTRACEPATTERN() macro can be used. This takes a single argument consisting of the new trace pattern. Using this macro is equivalent to calling the function OTC_TracePattern::set().
As with the OTC_TraceTag class, the OTC_TracePattern class can be used in conjunction with instances of the OTC_TraceSwitch class in logical expressions. You can also use the class in conjunction with instances of the OTC_TraceTag class. For example:
OTCLIB_STATIC_TRACEPATTERN(MODULE,
"$Source: /home/ose/examples/_tracepat.cc $");
OTCLIB_TRACETAG(LIBRARY);
void function()
{
OTCLIB_TRACESWITCH(HERE,MODULE||LIBRARY);
OTCLIB_TRACER(HERE) << "this module" << endl;
// ...
}