IOChannel¶
What is IOChannel?¶
IOChannel is designed as a wrapper and, depending on usage, a replacement
for std::cout
and printf()
. Its sports a number of unique and useful
features.
Multiple asynchronous outputs.
Message priorities (verbosity).
Message categories.
Built-in output formatting.
Advanced memory tools.
Setting up IOChannel¶
Including IOChannel¶
To include IOChannel, use the following:
#include "pawlib/iochannel.hpp"
IOChannel Instance¶
For convenience, a single static global instance of IOChannel, ioc
,
exists in that header. It is suitable for most purposes, though a custom
iochannel instance may be declared. All inputs and outputs that the developer
wishes to interface with one another via this class must share the same
instance.
Documentation Assumptions¶
For purposes of expediency, the default global static instance ioc
will be used in this documentation. All namespaces outside of the
scope of PawLIB will be stated explicitly.
Concepts¶
IOChannel uses two unique concepts, Verbosity and Category, to determine where and how a message is routed.
Category¶
The benefit to having categories on messages is that you can route different kinds of messages to different outputs. For example, you might send all errors and warnings to a debug terminal, and reserve “normal” messages for game notifications.
Category |
Enum |
Use |
---|---|---|
Normal |
|
Regular use messages, especially those you want the user to see. |
Warning |
|
Warnings about potential problems. |
Error |
|
Error messages. |
Debug |
|
Messages that might help you track down problems. |
Testing |
|
Messages related solely to testing. |
All |
|
All of the above. |
One of the advantages of this system is that you can actually leave messages in the code, and just control when and how they are processed and broadcast. This means you can actually ship with debugging statements still alive in the code, allowing you to diagnose problems on any machine.
You can control which of these categories messages are broadcast from using the echo settings (Internal Broadcast Settings (Echo)) and signals (Category Signals (signal_c_...)).
Verbosity¶
Some messages we need to see every time, and others only in special circumstances. This is what verbosity is for.
Verbosity |
Enum |
Use |
---|---|---|
Quiet |
|
Only essential messages and errors. For normal end-use. Shipping default. |
Normal |
|
Common messages and errors. For common and normal end-user testing. |
Chatty |
|
Most messages and errors. For detailed testing and debugging. |
TMI |
|
Absolutely everything. For intense testing, detailed debugging, and driving the developers crazy. |
One example of verbosity in action would be in debugging messages. A
notification about a rare and potentially problematic function being called
might be IOVrb::normal
, while the output of a loop iterator would probably
be IOVrb::tmi
.
You can control which of these categories messages are broadcast from using the echo settings (Internal Broadcast Settings (Echo)) and signals (Verbosity Signals (signal_v_...)).
Output¶
General¶
All output is done using the stream insertion (<<
) operator, in the same
manner as with std::cout
. Before a message is broadcast, a stream
control flags such as IOCtrl::endl
must be passed.
IOCtrl::endl
serves as an “end of transmission” [EoT] flag, clears any
formatting set during the stream, and inserts a final newline character before
flushing the stream. Thus, \n
is not needed if the output should be
displayed on a single line. This functionality also allows a single
transmission to be split up over multiple lines, if necessary. Other stream
control enumerations have different behaviors. (See Stream Control)
ioc << "This is the first part. ";
//Some more code here.
ioc << "This is the second part." << IOCtrl::endl;
Strings¶
IOChannel natively supports string literals, cstring (char arrays),
std::string
, and onestring
.
These are passed in using the <<
operator, as with anything being output
via IOChannel. The message will not be broadcast until an EoT
(end-of-transmission) flag is passed.
ioc << "Hello, world!" << IOCtrl::endl;
//OUTPUT: "Hello, world!"
char* cstr = "I am a Cstring.\0";
ioc << cstr << IOCtrl::endl;
//OUTPUT: "I am a Cstring."
std::string stdstr = "I am a standard string.";
ioc << stdstr << IOCtrl::endl;
//OUTPUT: "I am a standard string."
Formatting¶
Cross-platform output formatting is built in to IOChannel. This means that formatting can be set using the IOFormat flags, and it will display correctly on each output and environment.
ioc << IOFormatTextAttr::bold << IOFormatTextFG::red << "This is bold, red text. "
<< IOFormatTextAttr::underline << IOFormatTextFG::blue << IOFormatTextBG::yellow << "This is bold, underline, blue text with a yellow background. "
<< IOFormatTextAttr::none << IOFormatTextFG::none << IOFormatTextBG::none << "This is normal text."
<< IOCtrl::endl;
//The output is exactly what you'd expect.
Important
Currently, only ANSI is used. Formatting-removed and an easy-to-parse formatting flag system for custom outputs will be added soon.
Alternative, you can use the IOFormat
object to store multiple flags.
(See Formatting Objects)
Variable Input¶
IOChannel supports all basic C/C++ data types.
Boolean (
bool
)Char (
char
)Integer (
int
) and its various forms.Float (
float
)Double (
double
)
Boolean¶
Output for boolean is pretty basic and boring.
bool foo = true;
ioc << foo << IOCtrl::endl;
//OUTPUT: "TRUE"
The output style can be adjusted, however, using the IOFormatBool::
flags.
bool foo = true;
ioc << IOFormatBool::lower << foo << IOCtrl::endl;
//OUTPUT: "true"
ioc << IOFormatBool::upper << foo << IOCtrl::endl;
//OUTPUT: "True"
ioc << IOFormatBool::caps << foo << IOCtrl::endl;
//OUTPUT: "TRUE"
ioc << IOFormatBool::numeral << foo << IOCtrl::endl;
//OUTPUT: "1"
Char¶
Since char can represent both an integer and a character, IOChannel lets
you display it as either. By default, IOChannel displays the char as a literal
character. Using the IOFormatCharValue::as_int
flag forces it to print as
an integer.
char foo = 'A';
ioc << "Character " << foo << " has ASCII value "
<< IOFormatCharValue::as_int << foo << IOCtrl::endl;
//OUTPUT: Character A has ASCII value 65
When output as an integer, char can be used with all of the enumerations for int (see that section).
Integer¶
An int
can be represented in any base (radix) from binary (base 2) to
base 35 using the IOFormatBase::
flags.
int foo = 12345;
ioc << "Binary: " << IOFormatBase::bin << foo << IOCtrl::endl;
ioc << "Octal: " << IOFormatBase::oct << foo << IOCtrl::endl;
ioc << "Decimal: " << IOFormatBase::dec << foo << IOCtrl::endl;
ioc << "Dozenal: " << IOFormatBase::doz << foo << IOCtrl::endl;
ioc << "Hexadecimal: " << IOFormatBase::hex << foo << IOCtrl::endl;
ioc << "Base 31: " << IOFormatBase::b31 << foo << IOCtrl::endl;
/*OUTPUT:
Binary: 11000000111001
Octal: 30071
Decimal: 12345
Dozenal: 7189
Hexadecimal: 3039
Base 31: cq7
*/
In bases larger than decimal (10), the letter numerals can be output as
lowercase or uppercase (default) using the IOFormatNumCase::
flags.
int foo = 187254;
ioc << "Hexadecimal Lower: " << IOFormatBase::hex << foo << IOCtrl::endl;
ioc << "Hexadecimal Upper: " << IOFormatNumCase::upper
<< IOFormatBase::hex << foo << IOCtrl::endl;
/*OUTPUT:
Hexadecimal Lower: 2db76
Hexadecimal Upper: 2DB76
*/
Float and Double¶
Float and Double can only be output in base 10 directly. (Hexadecimal output is only possible through a pointer memory dump. See that section.) However, the significands (the number of digits after the decimal point) and use of scientific notation can be modified. By default, significands is 14, and use of scientific notation is automatic for very large and small numbers.
Significands can be modified using the IOFormatSignificands(#)
flag.
Scientific notation can be turned on with IOFormatSciNotation::on
,
and off using IOFormatSciNotation::none
. It can also be reset to automatic
with IOFormatSciNotation::automatic
.
float foo = 12345.12345678912345;
ioc << "Significands 5, no sci: " << IOFormatSignificands(5) << foo << IOCtrl::endl;
ioc << "Significands 10, sci: " << IOFormatSignificands(10)
<< IOFormatSciNotation::on << foo << IOCtrl::endl;
/*OUTPUT:
Significands 5, no sci: 12345.12304
Significands 10, sci: 1.2345123046e+4
*/
Both types work the same.
Pointer Output¶
One of the most powerful features of IOChannel is its handling of pointers. In addition to printing the value at known pointer types, it can print the address or raw memory for ANY pointer, even for custom objects.
Pointer Value¶
By default, IOChannel will attempt to print the value at the pointers. This
can also be forced using IOFormatPtr::value
.
int foo = 12345;
int* fooptr = &foo;
ioc << "Value of foo: " << IOFormatPtr::value << fooptr << IOCtrl::endl;
char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Value of bar: " << bar << IOCtrl::endl;
/*OUTPUT:
Value of foo: 12345
Value of bar: My name is Bob, and I am a coder.
*/
Pointer Address¶
IOChannel can print out the address of the pointer in hexadecimal using
IOFormatPtr::address
. It displays with lowercase letter numerals by default,
though these can be displayed in uppercase using IOFormatNumCase::upper
.
It is capable of doing this with any pointer, even for custom objects.
int foo = 12345;
int* fooptr = &foo;
ioc << "Address of foo: " << IOFormatPtr::address << fooptr << IOCtrl::endl;
char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Address of bar: " << IOFormatPtr::address << IOFormatNumCase::upper
<< bar << IOCtrl::endl;
/*OUTPUT:
Address of foo: 0x7ffc33518308
Address of bar: 0x405AF0
*/
Pointer Memory Dump¶
IOChannel is capable of dumping the raw memory at any pointer using
IOFormatPtr::memory
. The function is safe for pointers to most objects and
atomic types, as the memory dump will automatically determine the size
and will never overrun the size of the variable. With char pointers (cstring),
the only danger is when the cstring is not null terminated.
Spacing can be added between bytes (IOFormatMemSep::byte
) and bytewords
(IOFormatMemSep::word
), or both (IOFormatMemSep::all
). By default,
the memory dumps with no spacing (IOFormatMemSep::none
).
int foo = 12345;
int* fooptr = &foo;
ioc << "Memory dump of foo: " << IOFormatPtr::memory << IOFormatMemSep::byte
<< fooptr << IOCtrl::endl;
char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Memory dump of bar: " << IOFormatPtr::memory << IOFormatMemSep::all
<< bar << IOCtrl::endl;
/*OUTPUT:
Memory dump of foo: 39 30 00 00
Memory dump of bar: 4d 79 20 6e 61 6d 65 20 | 69 73 20 42 6f 62 2c 20 | 61 6e 64 20 49 20 61 6d | 20 61 20 63 6f 64 65 72 | 2e 00
*/
The following dumps the raw memory for a custom object.
//Let's define a struct as our custom object, and make an instance of it.
struct CustomStruct
{
int foo = 12345;
double bar = 123.987654321;
char faz[15] = "Hello, world!\0";
void increment(){foo++;bar++;}
};
CustomStruct blah;
ioc << IOFormatPtr::memory << IOFormatMemSep::all << &blah << IOCtrl::endl;
/*OUTPUT:
39 30 00 00 00 00 00 00 | ad 1c 78 ba 35 ff 5e 40 | 48 65 6c 6c 6f 2c 20 77 | 6f 72 6c 64 21 00 00 00
*/
You can also read memory from a void pointer, though you must specify the
number of bytes to read using IOMemReadSize()
.
Warning
This feature must be used with caution, as reading too many bytes can trigger segfaults or any number of memory errors. Use the sizeof operator in the read_bytes() argument to prevent these types of problems. (See code).
Bitset¶
IOChannel is able to intelligently output the contents of any bitset. It
temporarily forces use of the IOFormatPtr::memory
flag to ensure proper
output.
One may use any of the IOFormatMemSep::
flags to control the
style of output. By default, IOFormatMemSep::none
is used.
bitset<32> foo = bitset<32>(12345678);
ioc << IOFormatMemSep::all << foo << IOCtrl::endl;
/* OUTPUT:
4e 61 bc 00
*/
Formatting Objects¶
If you find yourself regularly using particular formatting flags
(IOFormat...::
), you can store them in an IOFormat object for reuse.
Flags are passed into the IOFormat
object with the stream insertion
operator (<<
), and then the IOFormat
object itself can be passed to
the IOChannel.
IOFormat fmt;
fmt << IOFormatTextAttr::bold << IOFormatTextFG::red << IOFormatTextBG::black;
ioc << fmt << "This is bold, red text on a black background." << IOCtrl::endl;
ioc << fmt << IOFormatBG::blue << "This is bold, red text on a blue background."
<< IOCtrl::endl;
As you can see, anything passed to the IOChannel after the IOFormat
object overrides prior options.
IOFormat supports all the flags beginning with IOFormat...
.
Stream Control¶
There are multiple enums for controlling IOChannel’s output.
For example, one might want to display progress on the same line, and then move to a new line for a final message. This can be accomplished via…
ioc << "Let's Watch Progress!" << IOCtrl::endl;
ioc << fg_blue << ta_bold;
for(int i=0; i<100; i++)
{
//Some long drawn out code here.
ioc << i << "%" << IOCtrl::sendc;
}
ioc << io_endl;
ioc << "Wasn't that fun?" << io_endl;
/* FINAL OUTPUT:
Let's Watch Progress!
100%
Wasn't that fun?
*/
The complete list of stream controls is as follows. Some notes…
EoM indicates “End of Message”, meaning IOChannel will broadcast the message at this point.
n is a newline.
r is simply a carriage return (move to start of current line).
Clear means all formatting flags are reset to their defaults.
Flush forces stdout to refresh. This is generally necessary when overwriting a line or moving to a new line after overwriting a previous one.
Command |
EoM |
Clear |
r |
n |
Flush |
---|---|---|---|---|---|
|
X |
||||
|
X |
||||
|
X |
X |
|||
|
X |
X |
X |
X |
|
|
X |
X |
X |
X |
|
|
X |
||||
|
X |
X |
X |
||
|
X |
X |
X |
||
|
X |
||||
|
X |
Cursor Movement¶
IOChannel can move the cursor back and forth on ANSI-enabled terminals using the IOCursor::left and IOCursor::right flags.
std::string buffer;
ioc << "Hello, world!"
<< IOCursor::left
<< IOCursor::left
<< IOCtrl::end;
std::getline(std::cin, buffer);
/* Will now wait for user input, while displaying "Hello, world!"
* with the cursor highlighting the 'd' character.
*/
Important
Currently, only ANSI is used. Windows support, formatting-removed, and an easy-to-parse formatting flag system for custom outputs will be added soon.
Internal Broadcast Settings (Echo)¶
IOChannel can internally output to either printf()
or std::cout
(or neither). By default, it uses printf(). However, as stated, this can be
changed.
IOChannel’s internal output also broadcasts all messages by default. This can also be changed.
These settings are modified by passing a IOEchoMode::
flag to the
configure_echo()
member function.
//Set to use `std::cout`
ioc.configure_echo(IOEchoMode::cout);
//Set to use `printf` and show only error messages (any verbosity)
ioc.configure_echo(IOEchoMode::printf, IOVrb::tmi, IOCat::error);
//Set to use `cout` and show only "quiet" verbosity messages.
ioc.configure_echo(IOEchoMode::cout, IOVrb::quiet);
//Turn off internal output.
ioc.configure_echo(IOEchoMode::none);
External Broadcast with Signals¶
One of the primary features of IOChannel is that it can be connected to multiple outputs using signals. Examples of this might be if you want to output to a log file, or display messages in a console in your interface.
Main Signal (signal_all
)¶
The main signal is signal_all
, which requires a callback function of the
form void callback(std::string, IOVrb, IOCat)
,
as seen in the following example.
//This is our callback function.
void print(std::string msg, IOVrb vrb, IOCat cat)
{
//Handle the message however we want.
std::cout << msg;
}
//We connect the callback function to `signal_all` so we get all messages.
ioc.signal_all.add(&print);
Category Signals (signal_c_...
)¶
Almost all categories have a signal: signal_c_normal
, signal_c_warning
,
signal_c_error
, signal_c_testing
, and signal_c_debug
.
Note
IOCat::all
is used internally, and does not have a signal.
Use signal_all
instead.
The callbacks for category signals require the form
void callback(std::string, IOVrb)
. Below is an example.
//This is our callback function.
void print_error(std::string msg, IOVrb vrb)
{
//Handle the message however we want.
std::cout << msg;
}
//We connect the callback function to signal_c_error to get only error messages.
ioc.signal_c_error.add(&print_error);
Verbosity Signals (signal_v_...
)¶
Each verbosity has a signal: signal_v_quiet
, signal_v_normal
,
signal_v_chatty
, and signal_v_tmi
. A signal is broadcast when any
message of that verbosity or lower is transmitted.
The callbacks for verbosity signals require the form
void callback(std::string, IOCat)
. Below is an example inside
the context of a class.
class TestClass
{
public:
TestClass(){}
void output(std::string msg, IOCat cat)
{
//Handle the message however we want.
std::cout << msg;
}
~TestClass(){}
};
TestClass testObject;
ioc.signal_v_normal.add(&testObject, TestClass::output)
Flag Lists¶
Category (IOCat::
)¶
Flag |
Use |
---|---|
|
No category; NEVER broadcasted. Does not have a correlating signal. |
|
The default value - anything that doesn’t fit elsewhere. |
|
Warnings, but not necessarily errors. |
|
Error messages. |
|
Debug messages, such as variable outputs. |
|
Messages in tests. (Goldilocks automatically suppresses these during benchmarking.) |
|
All message categories. Does not have a correlating signal. |
Cursor Control (IOCursor::
)¶
Flag |
Use |
---|---|
|
Moves the cursor left one position. |
|
Moves the cursor right one position. |
Echo Mode (IOEchoMode::
)¶
Note
These cannot be passed directly to IOChannel.
Flag |
Use |
---|---|
|
No internal output. |
|
Internal output uses |
|
Internal output uses |
Base/Radix Format (IOFormatBase::
)¶
Flag |
Base |
---|---|
|
2 |
|
2 |
|
3 |
|
3 |
|
4 |
|
4 |
|
5 |
|
5 |
|
6 |
|
6 |
|
7 |
|
7 |
|
8 |
|
8 |
|
9 |
|
10 |
|
10 |
|
11 |
|
11 |
|
12 |
|
12 |
|
12 |
|
13 |
|
13 |
|
14 |
|
14 |
|
15 |
|
15 |
|
16 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 |
|
36 |
Boolean Format (IOFormatBool::
)¶
Flag |
Use |
---|---|
|
Lowercase - “true” or “false” |
|
Uppercase - “True” or “False” |
|
All caps - “TRUE” or “FALSE” |
|
Binary numerals - “0” or “1” |
|
“Yea” or “Nay” |
Char Value (IOFormatCharValue::
)¶
Enum |
Action |
---|---|
|
Output chars as ASCII characters. |
|
Output chars as integers. |
Memory Separators (IOFormatMemSep::
)¶
Enum |
Action |
---|---|
|
Output memory dump as one long string. |
|
Output memory dump with spaces between bytes. |
|
Output memory dump with bars between words (8 bytes). |
|
Output memory dump with spaces between bytes and bars between words. |
Numeral Case (IOFormatNumCase::
)¶
Enum |
Action |
---|---|
|
Print all letter digits as lowercase. |
|
Print all letter digits as uppercase. |
Pointer Format (IOFormatPtr::
)¶
Enum |
Action |
---|---|
|
Print the value at the address. |
|
Print the actual memory address. |
|
Dump the hexadecimal representation of the memory at the address. |
Scientific Notation Format (IOFormatSciNotation::
)¶
Enum |
Action |
---|---|
|
No scientific notation. |
|
Automatically select the best option. |
|
Force use of scientific notation. |
Warning
IOFormatSciNotation::none
has been known to cause truncation
in very large and very small values, regardless of significands.
Significands(IOFormatSignificands()
)¶
IOFormatSignificands(n)
where n
is the significands, as an integer
representing the number of significands.
Text Attributes(IOFormatTextAttr::
)¶
Enum |
Action |
---|---|
|
Turn off all attributes. |
|
Bold text. |
|
Underlined text. |
|
Invert foreground and background colors. |
Text Background Color(IOFormatTextBG::
)¶
Enum |
Action |
---|---|
|
Default text background. |
|
Black text background. |
|
Red text background. |
|
Green text background. |
|
Yellow text background. |
|
Blue text background. |
|
Meganta text background. |
|
Cyan text background. |
|
White text background. |
Text Foreground Color(IOFormatTextFG::
)¶
Enum |
Action |
---|---|
|
Default text foreground. |
|
Black text foreground. |
|
Red text foreground. |
|
Green text foreground. |
|
Yellow text foreground. |
|
Blue text foreground. |
|
Meganta text foreground. |
|
Cyan text foreground. |
|
White text foreground. |
Memory Dump Read Size (IOMemReadSize()
)¶
IOMemReadSize(n)
where n
is the number of bytes to read and print,
starting at the memory address. Only used with void pointers.
Warning
Misuse triggers undefined behavior, including SEGFAULT. Use with caution.
Verbosity (IOVrb::
)¶
Enum |
Use |
---|---|
|
Only essential messages and errors. For normal end-use. Shipping default. |
|
Common messages and errors. For common and normal end-user testing. |
|
Most messages and errors. For detailed testing and debugging. |
|
Absolutely everything. For intense testing, detailed debugging, and driving the developers crazy. |