Page MenuHomePhabricator

Updated 2,465 Days AgoPublic


'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

IOChannel Instance

Use of IOChannel requires #include <iochannel.hpp>.

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.


IOChannel uses a number of enumerations and data types for formatting, data manipulation, and advanced usage. These are in the pawlib::ioformat namespace. To save typing, it may be helpful to import that particular namespace via using namespace pawlib::ioformat;, even if the primary namespace pawlib is not imported.

Documentation Assumptions

For purposes of expediency, the default global static instance pawlib::ioc will be used in this documentation. Furthermore, we will assume that using namespace pawlib::ioformat; and using namespace pawlib are being used. All namespaces outside of the scope of PawLIB will be stated explicitly.


IOChannel uses two unique concepts: Verbosity and 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.

Normalcat_normalRegular use messages, especially those you want the user to see.
Warningcat_warningWarnings about potential problems.
Errorcat_errorError messages.
Debugcat_debugMessages that might help you track down problems.
Allcat_allAll 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. No more commenting out (and then losing) your debugging print statements!


Some messages we need to see every time, and others only in special circumstances. This is what verbosity is for.

Quietvrb_quietOnly essential messages and errors. For normal end-use. Shipping default.
Normalvrb_normalCommon messages and errors. For common and normal end-user testing.
Chattyvrb_chattyMost messages and errors. For detailed testing and debugging.
Too Much Informationvrb_tmiAbsolutely 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 vrb_normal, while the output of a loop iterator would probably be vrb_tmi.



All input is done using the stream insertion (<<) operator, in the same manner as with std::cout. Before a message is broadcast, the io_end enumeration must be passed.

io_end serves both as an "end of transmission" flag and a final newline character. 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. (See Stream Control)

ioc << "This is the first part. ";
//Some more code here.
ioc << "This is the second part." << io_end;

`io_end` also clears any formatting set during the stream.

//OUTPUT: "This is the first part. This is the second part."


IOChannel natively supports string literals, cstring (char arrays), std::string, and pawlib::string.

IMPORTANT: pawlib::string support is upcoming.

These are passed in using the << operator, as with anything being input into IOChannel. The message will not be broadcast until io_end is passed.

ioc << "Hello, world!" << io_end;
//OUTPUT: "Hello, world!"

char* cstr = "I am a Cstring.\0";
ioc << cstr << io_end;
//OUTPUT: "I am a Cstring."

std::string stdstr = "I am a standard string.";
ioc << stdstr << io_end;
//OUTPUT: "I am a standard string."


Cross-platform output formatting is built in to IOChannel. This means that formatting can be set using the IOFormat enumerations, and it will display correctly on each output and environment.

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.
ioc << ta_bold << fg_red << "This is bold, red text. " << ta_underline << fg_blue << bg_yellow << "This is bold, underline, blue text with a yellow background. " << ta_none << fg_none << bg_none << "This is normal text." << io_end;
//The output is exactly what you'd expect.

Variable Input

IOChannel supports all basic C/C++ data types.

  • Boolean (bool)
  • Char (char)
  • Integer (int) and its various forms.
  • Float (float)
  • Double (double)


Output for boolean is pretty basic and boring.

bool foo = true;
ioc << foo << io_end;

The output style can be adjusted, however, using the bool_ enumerations.

bool foo = true;
ioc << bool_lower << foo << io_end;
//OUTPUT: "true"
ioc << bool_upper << foo << io_end;
//OUTPUT: "True"
ioc << bool_caps << foo << io_end;
ioc << bool_numeral << foo << io_end;
//OUTPUT: "1"


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 char_int flag forces it to print as an integer.

char foo = 'A';
ioc << "Character " << foo << " has ASCII value " << char_int << foo << io_end;
//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).


An int can be represented in any base from binary (base 2) to base 35 using the base enumerations.

int foo = 12345;
ioc << "Binary: " << base_bin << foo << io_end;
ioc << "Octal: " << base_oct << foo << io_end;
ioc << "Decimal: " << base_dec << foo << io_end;
ioc << "Dozenal: " << base_doz << foo << io_end;
ioc << "Hexadecimal: " << base_hex << foo << io_end;
ioc << "Base 31: " << base_31 << foo << io_end;

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 (default) or uppercase.

int foo = 187254;
ioc << "Hexadecimal Lower: " << base_hex << foo << io_end;
ioc << "Hexadecimal Upper: " << num_upper << base_hex << foo << io_end;

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 precision and use of scientific notation can be modified. By default, precision is 14, and use of scientific notation is automatic for very large and small numbers.

Precision can be modified using the set_precision(#) function. Scientific notation can be turned on with sci_on, and off using sci_none. It can also be reset to automatic with sci_auto.

float foo = 12345.12345678912345;
ioc << "Precision 5, no sci: " << set_precision(5) << foo << io_end;
ioc << "Precision 10, sci: " << set_precision(10) << sci_on << foo << io_end;

Precision 5, no sci: 12345.12304
Precision 10, sci: 1.2345123046e+4

Both types work the same.

Pointer Input

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 ptr_value.

int foo = 12345;
int* fooptr = &foo;
ioc << "Value of foo: " << ptr_value << fooptr << io_end;

char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Value of bar: " << bar << io_end;

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 'ptr_address'. It displays with lowercase letter numerals by default, though these can be displayed in uppercase using num_upper. It is capable of doing this with any pointer, even for custom objects.

int foo = 12345;
int* fooptr = &foo;
ioc << "Address of foo: " << ptr_address << fooptr << io_end;

char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Address of bar: " << ptr_address << num_upper << bar << io_end;

Address of foo: 0x7ffc33518308
Address of bar: 0x405AF0

Pointer Memory Dump

IOChannel is capable of dumping the raw memory at any pointer using ptr_memory. The function is safe for most known pointer types (bool, int, float, and double), as the memory dump will not 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 (mem_bytesep) and bytewords (mem_wordsep), or both (mem_allsep). By default, the memory dumps with no spacing (mem_nosep).

int foo = 12345;
int* fooptr = &foo;
ioc << "Memory dump of foo: " << ptr_memory << mem_bytesep << fooptr << io_end;

char* bar = "My name is Bob, and I am a coder.\0";
ioc << "Memory dump of bar: " << ptr_memory << mem_allsep << bar << io_end;

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

You can print memory from any pointer, though you must specify the number of bytes to read using read_bytes().

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).

The following is the safest way to dump 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 << ptr_memory << mem_allsep << read_size(sizeof blah) << &blah << io_end;
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

Stream Control

There are multiple enums for controlling IOChannel's output. All of these are compatible with std::cout and printf.

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!" << io_end;
ioc << fg_blue << ta_bold << io_send_keep;
for(int i=0; i<100; i++)
    //Some long drawn out code here.
    ioc << i << "%" << io_show_keep;
ioc << io_end << io_flush;
ioc << "Wasn't that fun?" << io_end;

Let's Watch Progress!
Wasn't that fun?

The complete list of stream controls is as follows. Some notes...

  • \n is a newline.
  • \r is simply a carriage return (move to start of current line).
  • EoM indicates "End of Message", meaning IOChannel will broadcast the message at this point.
  • R means "Remove Formatting", where all format tags are reset to their defaults.
  • F indicates a "flush", which forces stdout to refresh. This is generally necessary when overwriting a line or moving to a new line after overwriting a previous one.


Internal Broadcast Settings

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 using the configure_echo() member function. (The arguments are all IOFormat enumerations).

//Set to use `std::cout`

//Set to use `printf` and show only error messages (any verbosity)
ioc.configure_echo(echo_printf, vrb_tmi, cat_error);

//Set to use `cout` and show only "quiet" verbosity messages.
ioc.configure_echo(echo_cout, vrb_quiet);

//Turn off internal output.

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, IOFormatVerbosity, IOFormatCategory), as seen in the following example.

//This is our callback function.
void print(std::string msg, IOFormatVerbosity vrb, IOFormatCategory cat)
    //Handle the message however we want.
    std::cout << msg;

//We connect the callback function to `signal_all` so we get all messages.

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: cat_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, IOFormatVerbosity). Below is an example.

//This is our callback function.
void print_error(std::string msg, IOFormatVerbosity vrb)

//Handle the message however we want.
std::cout << msg;


//We connect the callback function to signal_c_error to get only error messages.

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, IOFormatCategory). Below is an example inside the context of a class (to demonstrate using sigc::mem_fun instead of sigc::ptr_fun).

class TestClass
        void output(std::string msg, IOFormatCategory cat)
            //Handle the message however we want.
            std::cout << msg;

            ioc.signal_v_normal.connect(sigc::mem_fun(this, &TestClass::output));

Enumeration Lists

Base/Radix (IOFormatBase)


Boolean Format (IOFormatBool)

bool_lowerLowercase - "true" or "false"
bool_upperUppercase - "True" or "False"
bool_capsAll caps - "TRUE" or "FALSE"
bool_numBinary numerals - "0" or "1"
bool_scottBSS [Team] Standard - "Yea" or "Nay"

Category (IOFormatCategory)

cat_normalThe default value - anything that doesn't fit elsewhere.
cat_warningWarnings, but not necessarily errors.
cat_errorError messages.
cat_debugDebug messages, such as variable outputs.
cat_testingMessages in tests. (Goldilocks automatically suppresses these during benchmarking.)
cat_allAll message categories. Does not have a correlating signal.

Echo Mode (IOEchoMode)

NOTE: These cannot be passed directly to IOChannel.
echo_noneNo internal output.
echo_printfInternal output uses printf().
echo_coutInternal output uses std::cout.

Memory Separators (IOFormatMemorySeperators)

mem_nosepOutput memory dump as one long string.
mem_bytesepOutput memory dump with spaces between bytes.
mem_wordsepOutput memory dump with bars between words (8 bytes).
mem_allsepOutput memory dump with spaces between bytes and bars between words.

Numeral Case (IOFormatNumeralCase)

num_lowerPrint all letter digits as lowercase.
num_upperPrint all letter digits as uppercase.

Pointer (IOFormatPointer)

ptr_valuePrint the value at the address.
ptr_addressPrint the actual memory address.
ptr_memoryDump the hexadecimal representation of the memory at the address.

Precision (set_precision)

set_precision(#) where # is the precision, as an integer representing the number of significands.

Memory Dump Read Size (read_size)

read_size(#) where # is the number of bytes to read and print, starting at the memory address.

WARNING: Misuse can cause SEGFAULT or other memory errors. Use with caution.

Scientific Notation (IOFormatSciNotation)

sci_noneNo scientific notation.
sci_autoAutomatically select the best option.
sci_onForce use of scientific notation.
WARNING: sci_none has been known to cause truncation in very large and very small values, regardless of precision.

Special (IOSpecial)

io_endEnd of message [EoM], newline, remove formatting.
io_end_keepEoM, newline, keep formatting.
io_sendEoM, remove formatting.
io_send_keepEoM, keep formatting.
io_endlineNewline, remove formatting.
io_endline_keepNewline, keep formatting.
io_showCarriage return and flush, EoM, remove formatting.
io_show_keepCarriage return and flush, EoM, retain formatting.
io_flushFlush only.
IMPORTANT: We'll be adding more later.

Text Attributes (IOFormatTextAttributes)

ta_noneTurn off all attributes.
ta_boldBold text.
ta_underlineUnderlined text.
ta_invertInvert foreground and background colors.

Text Background Color (IOFormatTextBG)

bg_noneDefault text background.
bg_blackBlack text background.
bg_redRed text background.
bg_greenGreen text background.
bg_yellowYellow text background.
bg_blueBlue text background.
bg_magentaMagenta text background.
bg_cyanCyan text background.
bg_whiteWhite text background.

Text Foreground Color (IOFormatTextFG)

fg_noneDefault text color.
fg_blackBlack text.
fg_redRed text.
fg_greenGreen text.
fg_yellowYellow text.
fg_blueBlue text.
fg_magentaMagenta text.
fg_cyanCyan text.
fg_whiteWhite text.

Verbosity/Priority (IOFormatVerbosity)

vrb_quietOnly essential messages and errors. For normal end-use.
vrb_normalCommon messages and errors. For common and normal end-user testing.
vrb_chattyMost messages and errors. For detailed testing and debugging.
vrb_tmiAbsolutely everything. For intense testing, detailed debugging, and driving the developers crazy.
Last Author
Last Edited
Nov 14 2015, 9:18 PM