Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslim_printer
[Package bslim]

Provide a mechanism to implement standard print methods. More...

Namespaces

namespace  bslim

Detailed Description

Outline
Purpose:
Provide a mechanism to implement standard print methods.
Classes:
bslim::Printer mechanism to implement standard print methods
Description:
This component provides a mechanism class, bslim::Printer, that, in many cases, simplifies the implementation of types providing a print method with the signature:
  bsl::ostream& print(bsl::ostream& stream,
                      int           level          = 0,
                      int           spacesPerLevel = 4) const;
      // Format this object to the specified output 'stream' at the (absolute
      // value of) the optionally specified indentation 'level' and return a
      // reference to 'stream'.  If 'level' is specified, optionally specify
      // 'spacesPerLevel', the number of spaces per indentation level for
      // this and all of its nested objects.  If 'level' is negative,
      // suppress indentation of the first line.  If 'spacesPerLevel' is
      // negative format the entire output on one line, suppressing all but
      // the initial indentation (as governed by 'level').  If 'stream' is
      // not valid on entry, this operation has no effect.
Note that all value-semantic types are expected to provide this method. bslim::Printer also supports generic containers, including those in BSL's standard library implementation, through use of standard conforming iterators and the bslalg::HasStlIterators trait. Use of the Printer mechanism provides a uniform style of output formatting:
  • Enclose the object's salient attributes with square brackets.
  • Prefix each attribute with the attribute's name, separated by an "equal" sign surrounded by space characters (" = ").
  • If the attributes are to be printed on multiple lines, then print them with one more level of indentation than that of the enclosing brackets. If any of the attributes are compositions, then the composite values must be printed with an additional level of indentation.
  • If the attributes are to be printed on a single line, then separate each value with a single space character.
  • For small, common types, such as bdlt::Date, the names of attributes, equal sign, and brackets may be omitted, with the entire value represented on a single line in a custom format. For example, the bdlt::Date::print method emits the date value in the format: 01JAN2001.
For example, consider a class having two attributes, ticker, represented by a bsl::string, and price, represented by a double. The output for a print method that produces standardized output for print(bsl::cout, 0, -4) (single-line output) is shown below:
  [ ticker = "ABC" price = 65.89 ]
Output for print(bsl::cout, 0, 4) (multi-line output) is shown below:
  [
      ticker = "ABC"
      price = 65.89
  ]
The Printer mechanism provides methods and method templates to format data as described above. Printer objects are instantiated with the target stream to be written to, and the values of the indentation level of the data, level, and the spaces per level, spacesPerLevel. The methods provided by Printer, printAttribute, printValue, printOrNull, printHexAddr and printForeign, use these values for formatting. The start and end methods print the enclosing brackets of the output. In order to generate the standard output format, start should be called before any of the other methods, and end should be called after all the other methods have been called.
Usage:
In the following examples, we examine the implementation of the print method of different types of classes using Printer.
Example 1: print Method for a Value-Semantic Class:
In this example, we demonstrate how to use Printer to implement the standard print function of a value-semantic class having multiple attributes. Suppose we have a class, StockTrade, that provides a container for a fixed set of attributes. A StockTrade object has four attributes, ticker, price, quantity, and optional notes:
  class StockTrade {
      // This class represents the properties of a stock trace.

      // DATA
      bsl::string                d_ticker;    // ticker symbol
      double                     d_price;     // stock price
      double                     d_quantity;  // quanity traded
      bsl::optional<bsl::string> d_notes;     // optional trade notes

    public:
      ...

      // ACCESSORS
      bsl::ostream& print(bsl::ostream& stream,
                          int           level = 0,
                          int           spacesPerLevel = 4) const
      {
          if (stream.bad()) {
              return stream;                                        // RETURN
          }

          bslim::Printer printer(&stream, level, spacesPerLevel);
          printer.start();
          printer.printAttribute("ticker",   d_ticker);
          printer.printAttribute("price",    d_price);
          printer.printAttribute("quantity", d_quantity);
          printer.printAttribute("notes",    d_notes);
          printer.end();

          return stream;
      }
  };
Sample output for StockTrade::print(bsl::cout, 0, -4):
  [ ticker = "IBM" price = 107.3 quantity = 200 notes = "XYZ" ]
Sample output for StockTrade::print(bsl::cout, 0, 4):
  [
      ticker = "IBM"
      price = 107.3
      quantity = 200
      notes = "XYZ"
  ]
Example 2: print Method for a Mechanism Class:
In this example, we discuss the implementation of print for a mechanism class. A mechanism class does not have any salient attributes that define its value (as a mechanism does not have a "value"). However, the print method may be implemented to output the internal state of an object of such a type, e.g., for debugging purposes.
For example, consider a memory manager class, BlockList, that maintains a linked list of memory blocks:
  class BlockList {
      // This class implements a low-level memory manager that allocates and
      // manages a sequence of memory blocks.

      // TYPES
      struct Block {
          // This 'struct' overlays the beginning of each managed block of
          // allocated memory, implementing a doubly-linked list of managed
          // blocks, and thereby enabling constant-time deletions from, as
          // well as additions to, the list of blocks.

          Block                                *d_next_p;       // next
                                                                // pointer

          Block                               **d_addrPrevNext; // enable
                                                                // delete

          bsls::AlignmentUtil::MaxAlignedType   d_memory;       // force
                                                                // alignment
      };

      // DATA
      Block            *d_head_p;      // address of first block of memory
                                       // (or 0)

      bslma::Allocator *d_allocator_p; // memory allocator; held, but not
                                       // owned

    public:
      // ...
      // ACCESSORS
      // ...
      bsl::ostream& print(bsl::ostream& stream,
                          int           level          = 0,
                          int           spacesPerLevel = 4) const;
  };
For the purposes of debugging, it may be useful to print the starting address of every memory block in a BlockList, which can be done using the printHexAddr method of the Printer class:
  bsl::ostream& BlockList::print(bsl::ostream& stream,
                                 int           level,
                                 int           spacesPerLevel) const
  {
      if (stream.bad()) {
          return stream;                                            // RETURN
      }

      bslim::Printer printer(&stream, level, spacesPerLevel);
      printer.start();
      for (Block *it = d_head_p; it; it = it->d_next_p) {
          printer.printHexAddr(it, 0);
      }
      printer.end();

      return stream;
  }
Sample output for 'BlockListprint(bsl::cout, 0, -4):
  [ 0x0012fab4 0x0012fab8 ]
Sample output for 'BlockListprint(bsl::cout, 0, 4):
  [
      0x0012fab4
      0x0012fab8
  ]
Example 3: Foreign (Third-Party) Classes, and Printing STL Containers:
In this example, we use a Printer object to help format the properties of a class supplied by a third-party that does not implement the standard print method. Consider a struct, ThirdPartyStruct, defined in /usr/include/thirdparty.h that has no standard print method. We will be using this struct within another class Customer, storing some Customer objects in a map, and printing the map.
  struct ThirdPartyStruct {
      // Suppose this struct is defined somewhere in
      // '/usr/include/thirdparty.h', we have no control over it and hence
      // cannot add a .print method to it.

      enum { PRIVATE  = 1,
             WRITABLE = 2 };

      short pid;              // process id
      short access_flags;     // options
      char  user_id[20];      // userid
  };
We create a struct MyThirdPartyStructPrintUtil:
  struct MyThirdPartyStructPrintUtil {
      static
      bsl::ostream& print(bsl::ostream&           stream,
                          const ThirdPartyStruct& data,
                          int                     level = 0,
                          int                      spacesPerLevel = 4);
          // You write this function in your own code to accommodate
          // 'ThirdPartyStruct'.
  };

  bsl::ostream& MyThirdPartyStructPrintUtil::print(
                                      bsl::ostream&           stream,
                                      const ThirdPartyStruct& data,
                                      int                     level,
                                      int                     spacesPerLevel)
  {
      bslim::Printer printer(&stream, level, spacesPerLevel);
      printer.start();
      printer.printAttribute("pid",          data.pid);
      printer.printAttribute("access_flags", data.access_flags);
      printer.printAttribute("user_id",      data.user_id);
      printer.end();

      return stream;
  }
We create a class Customer that has a ThirdPartyStruct in it:
  class Customer {
      // DATA
      bsl::string      d_companyName;
      ThirdPartyStruct d_thirdPartyStruct;
      bool             d_loyalCustomer;

    public:
      // CREATORS
      Customer() {}

      Customer(const bsl::string& companyName,
               short              pid,
               short              accessFlags,
               const bsl::string& userId,
               bool               loyalCustomer)
      : d_companyName(companyName)
      , d_loyalCustomer(loyalCustomer)
      {
          d_thirdPartyStruct.pid = pid;
          d_thirdPartyStruct.access_flags = accessFlags;
          bsl::strcpy(d_thirdPartyStruct.user_id, userId.c_str());
      }

      // ACCESSORS
      void print(bsl::ostream& stream,
                 int           level = 0,
                 int           spacesPerLevel = 4) const
      {
          bslim::Printer printer(&stream, level, spacesPerLevel);
          printer.start();
          printer.printAttribute("CompanyName", d_companyName);
          printer.printForeign(d_thirdPartyStruct,
                               &MyThirdPartyStructPrintUtil::print,
                               "ThirdPartyStruct");
          printer.printAttribute("LoyalCustomer", d_loyalCustomer);
          printer.end();
      }
  };
We then create some Customer objects and put them in a map:
  void myFunc()
  {
      bsl::map<int, Customer> myMap;
      myMap[7] = Customer("Honeywell",
                          27,
                          ThirdPartyStruct::PRIVATE,
                          "hw",
                          true);
      myMap[5] = Customer("IBM",
                          32,
                          ThirdPartyStruct::WRITABLE,
                          "ibm",
                          false);
      myMap[8] = Customer("Burroughs",
                          45,
                          0,
                          "burr",
                          true);
Now we print the map
      bslim::Printer printer(&cout, 0, 4);
      printer.start();
      printer.printValue(myMap);
      printer.end();
  }
The following is written to stdout:
  [
      [
          [
              5
              [
                  CompanyName = "IBM"
                  ThirdPartyStruct = [
                      pid = 32
                      access_flags = 2
                      user_id = "ibm"
                  ]
                  LoyalCustomer = false
              ]
          ]
          [
              7
              [
                  CompanyName = "Honeywell"
                  ThirdPartyStruct = [
                      pid = 27
                      access_flags = 1
                      user_id = "hw"
                  ]
                  LoyalCustomer = true
              ]
          ]
          [
              8
              [
                  CompanyName = "Burroughs"
                  ThirdPartyStruct = [
                      pid = 45
                      access_flags = 0
                      user_id = "burr"
                  ]
                  LoyalCustomer = true
              ]
          ]
      ]
  ]
Example 4: Printing Ranges, and Typed Pointers:
In this examples we demonstrate two capabilities of a bslim::Printer object: printing a range of elements using iterators and printing a pointer type.
The printValue or printAttribute methods of bslim::Printer will print out all of the elements in the range specified by a pair of iterator arguments, which can be of any type that provides appropriately behaving operators ++, *, and == (a non-void pointer would qualify).
When bslim encounters a single pointer of type TYPE *, where TYPE is neither void nor char, the pointer value is printed out in hex followed by printing out the value of TYPE. A compile error will occur if bslim is unable to print out TYPE.
As an example, we print out a range of pointers to sets.
First we create 3 sets and populate them with different values.
  typedef bsl::set<int> Set;

  Set s0, s1, s2;

  s0.insert(0);
  s0.insert(1);
  s0.insert(2);

  s1.insert(4);
  s1.insert(5);

  s2.insert(8);
Then, we store the addresses to those 3 sets into a fixed-length array:
  const Set *setArray[] = { &s0, &s1, &s2 };
  const int NUM_SET_ARRAY = sizeof setArray / sizeof *setArray;
Next we use printValue to print a range of values by supplying an iterator to the beginning and end of the range, in the address of setArray and the address one past the end of setArray:
  bslim::Printer printer(&cout, 0, -1);
  printer.printValue(setArray + 0, setArray + NUM_SET_ARRAY);
The expected output is:
  [ 0xffbfd688 [ 0 1 2 ] 0xffbfd678 [ 4 5 ] 0xffbfd668 [ 8 ] ]
Example 5: print Method for a Low-Level Value-Semantic Class:
For very simple classes, it may be desirable always to format the attributes on a single line. In this example, we discuss the print method formatting for such a low-level value-semantic class.
Usually, single-line or multi-line formatting options are specified by the value of the spacesPerLevel argument, but for a simple class that always prints on a single line, the only difference between the single- and multi-line cases is that a newline character is printed at the end of the output for the multi-line case. For such classes, the "name" of the attribute and the enclosing brackets may be omitted as well.
For example, consider a class, DateTz, having as attributes a local date and a time offset:
  class DateTz {
      // This 'class' represents a date value explicitly in a local time
      // zone.  The offset of that time (in minutes) from UTC is also part of
      // the value of this class.

    private:
      // DATA
      int d_localDate;  // date in YYYYMMDD format, local to the timezone
                        // indicated by 'd_offset'

      int d_offset;     // offset from UTC (in minutes)

    public:
      // ...
      // ACCESSORS
      bsl::ostream& print(bsl::ostream& stream,
                          int           level          = 0,
                          int           spacesPerLevel = 4) const;
      // ...
  };
The Printer class may be used in this case to print the start and end indentation by passing a suppressBracket flag to the start and end methods. The value itself can be written to the stream directly without using Printer. Note that to ensure correct formatting of the value in the presence of a call to setw on the stream, the output must be written to a bsl::ostringstream first; the string containing the output can then be written to the specified stream:
  bsl::ostream& DateTz::print(bsl::ostream& stream,
                              int           level,
                              int           spacesPerLevel) const
  {
     if (stream.bad()) {
         return stream;                                             // RETURN
     }

     bsl::ostringstream tmp;
     tmp << d_localDate;

     const char sign    = d_offset < 0 ? '-' : '+';
     const int  minutes = '-' == sign ? -d_offset : d_offset;
     const int  hours   = minutes / 60;

     // space usage: +-  hh  mm  nil
     const int SIZE = 1 + 2 + 2 + 1;
     char buf[SIZE];

     // Use at most 2 digits for 'hours'
     if (hours < 100) {
         bsl::sprintf(buf, "%c%02d%02d", sign, hours, minutes % 60);
     }
     else {
         bsl::sprintf(buf, "%cXX%02d", sign, minutes % 60);
     }

     tmp << buf;

     bslim::Printer printer(&stream, level, spacesPerLevel);
     printer.start(true);
     stream << tmp.str();
     printer.end(true);

     return stream << bsl::flush;
  }
Sample output for 'DateTzprint(bsl::cout, 0, -4):
  01JAN2011-0500
Sample output for 'DateTzprint(bsl::cout, 0, 4):
  01JAN2011-0500<\n>