[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5. vul: Utilities

Chapter summary: General utilities are often handy. vul has a few.

Like vbl, vul also provides some basic utilities. The difference is philosophical: vbl includes things that could be thought of as extending the STL, while vul provides just plain utilities which don't claim to be useful or general enough to incorporate into the STL, and may not follow the spirit of the STL. The key elements of this library are:

class: vul_file
class: vul_directory

File handling utilities, directory reading

class: vul_url

Downloading files over HTTP.

class template: vul_arg

Parse command-line arguments conveniently.

class: vul_redirector

Simplify redirection of standard output/error

class: vul_awk

Read text files, breaking each line into fields.

class: vul_reg_exp

Regular expression matching.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.1 Redirecting standard output: vul_redirector

The class vul_redirector is provided to simplify the task of filtering the output of vcl_cerr and vcl_cout, a common requirement in graphical applications. This encapsulates some of the subtleties of deriving from vcl_streambuf, providing a simpler interface.

The basic usage is to subclass from vul_redirector, implementing the putchunk method, which is then called whenever characters are ready for output. The vul_redirector constructor takes care of attaching the new buffer to the stream, and of restoring the original behaviour on destruction. Here is a simple example, which switches output on or off depending on the value of a global flag;

 
#include <vul/vul_redirector.h>

bool on = true;

struct my_redirector : vul_redirector {
  my_redirector(vcl_ostream& s): vul_redirector(s) {}
  int putchunk(char const* buf, int n) {
    if (on)
      return vul_redirector::put_passthru(buf, n);
    else
      return n;
  }
};

and here is a calling program which exercises the example.

 
int main(int argc, char* argv[])
{
  vcl_cerr << "hi\n";
  {
    my_redirector redir(vcl_cerr);
    on = false;
    vcl_cerr << "magic\n";
  }
  vcl_cerr << "what did I miss?\n";
  return 0;
}

When this program is run, the word magic is not displayed, because my_redirector::putchunk finds that on == false. Question, what to you think put_passthru does? What happens if you set on = true on line 6?


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.2 Complex output formatting: vul_printf

While it is possible to achieve all of the functionality of the C printf function in C++, it is very very difficult. There are many times when programs can be made clearer by the use of printf formatting, rather than the standard iostream operators. On the other hand, one needs iostreams for type-safe (and convenient) output of user-defined objects. Thus vul provides a stream-aware version, vul_printf:

 
vcl_ostream& vul_printf(vcl_ostream&, char const* format, ...);

so that one can say, for example,

 
vul_printf(vcl_cerr, "Line %05d, Code %-30s\n", __LINE__, code);

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3 Reading command-line arguments: vul_arg

My favourite bit of vul is the vul_arg header which provides the easiest way to parse command-line arguments that I've seen. The basic idea is that a minimal specification for a command-line argument includes: the argument's type, a variable to hold it, its flag, and possibly some descriptive text and a default value.

In the default, easy to use (and a bit naughty) form, each argument is declared anywhere in the program, like so:

 
   vul_arg<double> my_threshold("-fudge", "Twiddle fudge", 1.7);
   //     Type     Variable     Flag      Help text        Default

and when vul_arg_parse is called, all the arguments are gathered, and extracted from the command line. To use an argument anywhere in the program, use its () operator:

 
   vcl_cerr << "The threshold = " << my_threshold() << vcl_endl;

To check if an argument was changed from its default value, one can check bool my_threshold.set().

Here is a complete example which uses vul_arg. I tend to give these argument variables names beginning with a_, but don't let that put you off.

 
#include <vcl_iostream.h>
#include <vul/vul_arg.h>

vul_arg<double> a_naughty_global_arg("-hack", "Fudge", 1.2);

void main(int argc, char* argv[])
{
  vul_arg<char const*>     a_filename(0, "Input filename");
  vul_arg<bool>            a_fast("-fast", "Go fast", false);
  vul_arg_parse(argc, argv);

  vcl_cerr << "Filename [" << a_filename() << "]\n";
}

Passing a 0 as the flag string means that the argument is obligatory, and will be taken as the first unparsed word on the command line.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3.1 Required arguments

As shown in the example above, a very simple method to set an argument as obligatory is passing a 0 (or an empty string) as the flag string. If there are two or more arguments, the parser extracts these arguments using the order they appear on the command line.

In that case, there is not a flag string. This method to provide obligatory arguments is very common (i.e. when you use operating system shells). However, if you have many arguments or want a more readable interface, you could prefer a required argument which includes a flag string.

To add this functionality, vul includes a special constructor to indicate that the argument is required. If you want the paremeter to be obligatory, add the special value is_required as the third parameter. For example:

 
  vul_arg<int>    key      ("-key", "Required int", vul_arg<int>::is_required);
  //     Type     Variable   Flag      Help text      dummy parameter

Note that there is not a default value, because it does not make sense.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3.2 Help text

The help text supplied with each argument is used to provide a summary of options when the special argument -? is seen. Running this example with the -? flag produces the output.

 
Usage: ./example_vul_arg.exe [-hack float] string [-fast bool]

REQUIRED:
         string       Input filename  ['-']

Optional:
  Switch Type         Help [value]

   -hack float        Quick hack factor  [1.2]
   -fast bool         Go fast  [not set]

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3.3 Lists of numbers

A very useful specialization also exists to read ranges of numbers. Imagine a program called makemovie which operates on a list of frames, specified on the command line:

 
makemovie -frames 1:10,9:-1:1,0,0,0,0

These can be easily read into a vcl_list<int>:

 
#include <vcl_list.h>
vul_arg<vcl_list<int> >  a_frame_list("-frames", "List of indices");

The list will preserve the order specified on the command line, so in the above example, the result of printing the list would be

 
1 2 3 4 5 6 7 8 9 10 9 8 7 6 5 4 3 2 1 0 0 0 0

As a gratuitous STL example, here is the code that printed that list

 
vcl_copy(a_frame_list().begin(), a_frame_list().end(),
         vcl_ostream_iterator<int>(vcl_cout, " "));

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.3.4 More structured argument handling

Of course, it's disgraceful programming practice to throw args around ones program higgledy piggledy, so one can collect arguments in objects of type vul_arg_list.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.4 Timing operations: vul_timer

If you want to measure elapsed time, for example to find out how long a section of your program takes to run, use the vul_timer class. When a vul_timer is initialized, like so, it records the current time.

 
   vul_timer mytimer;

Later, you can find out the elapsed time using the real() method:

 
   vcl_cerr << "That took " << mytimer.real() << " milliseconds\n";

If you want to reset the timer to make a new measurement, use the mark() method.

 
   mytimer.mark();  // Reset and start counting from zero again.

If you are running other jobs on your computer, you might like to know how much time was used by your program alone. For that, one would use the user() method.

 
   vcl_cerr << "Of which " << mytimer.user() << "ms was actually me\n";

In general, it is better to use the real, "wall-clock" elapsed time, as the CPU time returned by user can fail to include work carried out on your program's behalf by the operating system. For example, if you're using a lot of memory, the system will swap pages in and out of virtual memory, and your program will run slowly, but user() will not report it.

Finally, there's a super-convenient print method, which is used to just print the real and user times to a stream without any formatting, for quick testing purposes. Here's an example

 
  vul_timer tic; // Start timing
  // do stuff
  tic.print(vcl_cerr); // Print times to vcl_cerr.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.5 Reading ASCII files: vul_awk

It is often convenient to read text files a line at a time, and split each line into space-separated fields. The vul_awk class is an easy way to do this. It also adds a few handy extras like stripping comments, and remembering the line number for error messages. It is used like this:

 
   vcl_ifstream thefile("myfile.txt");
   vul_awk awk(thefile); // initialize and read 1st line
   for (; awk; ++awk) {
     vcl_cerr << "Field 0 = " << awk[0] << vcl_endl;
     vcl_cerr << "Field 2 = " << awk[2] << vcl_endl;
   }

If myfile.txt contained the text

 
  dapple dawn-drawn falcon,
  solihull 1  grimsby 3

the above program would print

 
  Field 0 = dapple
  Field 2 = falcon,
  Field 0 = solihull
  Field 2 = grimsby

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.6 Regular expression parsing: vul_reg_exp

A regular expression allows a programmer to specify complex patterns that can be searched for and matched against the character string of a string object. In its simplest form, a regular expression is a sequence of characters used to search for exact character matches. However, many times the exact sequence to be found is not known, or only a match at the beginning or end of a string is desired. The vul regular expression class implements regular expression pattern matching as is found and implemented in many UNIX commands and utilities, notably perl. The perl code

 
   $filename =~ m"([a-z]+)\.h";
   print $1;

is written as follows in vxl

 
   vul_reg_exp re("([a-z]+)\\.h");
   re.find(filename);
   vcl_cerr << re.match(1);

The vul syntax is similar to perl's, although not quite as clean. Here are the metacharacters:

 
 ^        Matches at beginning of a line
 $        Matches at end of a line
 .        Matches any single character
[ ]       Matches any character(s) inside the brackets
[^ ]      Matches any character(s) not inside the brackets
 -        Matches any character in range on either side of a dash
 *        Matches preceding pattern zero or more times
 +        Matches preceding pattern one or more times
 ?        Matches preceding pattern zero or once only
()        Saves a matched expression and uses it in a later match.

Note that more than one of these metacharacters can be used in a single regular expression in order to create complex search patterns. For example, the pattern [^ab1-9] says to match any character sequence that does not begin with the characters "ab" followed by numbers in the series 1-9.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

5.7 Loading files using Internet protocols: vul_url

vul_url provides a useful means of downloading files over the internet. For example, if you used to have code like this

 
bool read_ascii(vcl_istream &);
...
int main() {
...
  vcl_ifstream  input(filename);
  if (!(!input))
    read_ascii(input);
  input.close;
...

You can get this code to work, even if filename begins with "http://" by modifying your code to look like.

 
bool read_ascii(vcl_istream &);
...
int main() {
...
  vcl_istream  *p_input = vul_url::open(filename);
  if (p_input != 0 && !(!(*input)))
    read_ascii(*input);
  delete input;
...

This will now work both when filename is an HTTP URL and when it is an ordinary filename.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated on May, 1 2013 using texi2html 1.76.