BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslim_printer

Detailed Description

Outline

Purpose

Provide a mechanism to implement standard print methods.

Classes

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:

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;
}
};
Definition bslstl_string.h:1281
Definition bslstl_optional.h:1861
Definition bslim_printer.h:601

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
// 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;
};
Definition bslma_allocator.h:457
AlignmentToType< BSLS_MAX_ALIGNMENT >::Type MaxAlignedType
Definition bsls_alignmentutil.h:282

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 'BlockList::print(bsl::cout, 0, -4):

[ 0x0012fab4 0x0012fab8 ]

Sample output for 'BlockList::print(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();
}
};
const CHAR_TYPE * c_str() const BSLS_KEYWORD_NOEXCEPT
Definition bslstl_string.h:6705
bsl::ostream & print(bsl::ostream &stream, const TYPE &object, int level=0, int spacesPerLevel=4)
Definition bdlb_printmethods.h:719

We then create some Customer objects and put them in a map:

void myFunc()
{
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);
Definition bslstl_map.h:619

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);
Definition bslstl_set.h:657
pair< iterator, bool > insert(const value_type &value)
Definition bslstl_set.h:2326

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
}
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;
}
Definition bslstl_ostringstream.h:175
void str(const StringType &value)
Definition bslstl_ostringstream.h:581

Sample output for 'DateTz::print(bsl::cout, 0, -4):

01JAN2011-0500

Sample output for 'DateTz::print(bsl::cout, 0, 4):

01JAN2011-0500<\n>