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:
-
-
- 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;
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 {
bsl::string d_ticker;
double d_price;
double d_quantity;
bsl::optional<bsl::string> d_notes;
public:
...
bsl::ostream& print(bsl::ostream& stream,
int level = 0,
int spacesPerLevel = 4) const
{
if (stream.bad()) {
return stream;
}
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: 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;
}
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 {
enum { PRIVATE = 1,
WRITABLE = 2 };
short pid;
short access_flags;
char user_id[20];
};
We create a struct MyThirdPartyStructPrintUtil
: struct MyThirdPartyStructPrintUtil {
static
bsl::ostream& print(bsl::ostream& stream,
const ThirdPartyStruct& data,
int level = 0,
int spacesPerLevel = 4);
};
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 {
bsl::string d_companyName;
ThirdPartyStruct d_thirdPartyStruct;
bool d_loyalCustomer;
public:
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());
}
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 {
private:
int d_localDate;
int d_offset;
public:
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;
}
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;
const int SIZE = 1 + 2 + 2 + 1;
char buf[SIZE];
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): Sample output for 'DateTzprint(bsl::cout, 0, 4):