Ch11.3: Manipulators

What Are Manipulators?

A manipulator is a small helper object that wraps a value (or values) and tells print / println / scan how to format or parse that value. Instead of format strings like "%x" or std::hex, fast_io expresses formatting in the type system: the manipulator is an object with its own type, so the compiler can check and inline it away completely.

All manipulators live in the namespace ::fast_io::manipulators, which has the convenient shorthand ::fast_io::mnp. Most programs just add:


using namespace ::fast_io::mnp;

and then use the manipulators directly. Every example below assumes this using-declaration.

Numeric Bases: hex, oct, bin, dec

The most common manipulators change the base used to print or scan an integer. hex, oct, bin, and dec produce lowercase digits. Their uppercase siblings hexupper / octupper / binupper exist too. For scanning, use the _get variants: hex_get, oct_get, bin_get, dec_get.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    ::std::size_t n{0xBEEFz};

    // Output
    println("hex    : ", hex(n));
    println("oct    : ", oct(n));
    println("bin    : ", bin(n));
    println("dec    : ", dec(n));
    println("hexup  : ", hexupper(n));

    // Input: read a and b as hexadecimal
    ::std::size_t a{}, b{};
    scan(hex_get(a), hex_get(b));
    println("sum = ", hex(a + b));
}

hex0x — Hex With 0x Prefix

hex0x prints an integer in lowercase hexadecimal with the usual 0x prefix. Use hex0xupper for uppercase digits and 0X. These are ideal when producing output that a human will read and then copy back into source code.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    ::std::size_t flags{0xCAFEz};
    // prints: flags = 0xcafe
    println("flags = ", hex0x(flags));
    // prints: flags = 0XCAFE
    println("flags = ", hex0xupper(flags));
}

boolalpha

By default, bool prints as 1 or 0. The boolalpha manipulator prints it as true / false instead (or uppercase TRUE / FALSE with the template argument).


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    bool ok{true};
    println("raw       : ", ok);              // 1
    println("alpha     : ", boolalpha(ok));    // true
    println("alpha up  : ", boolalpha<true>(ok)); // TRUE
}

Floating-Point Formats: fixed, scientific, general

Three manipulators control the notation used for floating-point output:

Each has a comma_ sibling (comma_fixed, comma_scientific, comma_general) that inserts thousands separators. An optional second argument controls the number of digits after the decimal point.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    double pi{3.14159265358979};
    double big{1234567.875};

    println("fixed       : ", fixed(pi));
    println("scientific  : ", scientific(pi));
    println("general     : ", general(pi));

    // Fixed precision: 2 digits after the decimal point
    println("fixed(2)    : ", fixed(pi, 2zu));

    // With thousands separators
    println("comma_fixed : ", comma_fixed(big, 2zu));
}

comma_decimal — Thousands Separators

When you only want commas (or other grouping separators) without changing the floating-point notation, the comma_* family is what you reach for. comma_decimal is the most common — it formats a number in plain decimal notation with group separators inserted.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    ::std::size_t population{8110700zu};
    // prints something like: 8,110,700
    println("people: ", comma_decimal(population));
}

Alignment: left, right, middle

left, right, and middle pad a value to a given field width. The first argument is the value, the second is the width, and an optional third argument is the fill character (defaults to space).


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    int n{-42};

    // default fill is ' '
    println("|", left(n, 10zu), "|");     // | -42       |
    println("|", right(n, 10zu), "|");    // |       -42 |
    println("|", middle(n, 10zu), "|");   // |    -42    |

    // custom fill character
    println("|", left(n, 10zu, '*'), "|");   // | -42*******|
    println("|", right(n, 10zu, '0'), "|");  // | 0000000-42|
}

chvw — Print an Integer as a Character

chvw(ch) treats an integral value as a character and prints it. This is useful when you have a char stored in an integer, or when you want to print a code point without going through a string.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    char c{'A'};
    // Prints the character A followed by its integer value
    println("char = ", chvw(c), " code = ", static_cast<int>(c));
}

rgvw — Range View

rgvw(range, sep) prints every element of a range, separated by the given string or character. It is the idiomatic way to dump the contents of a container without writing a loop. The separator may be a C string, a single character, or an os_c_str-like object.


#include <fast_io.h>
#include <fast_io_dsal/vector.h>

using namespace fast_io::io;

int main()
{
    ::fast_io::vector<int> v{1, 2, 3, 4, 5};
    // prints: 1, 2, 3, 4, 5
    println(rgvw(v, ", "));

    // Any separator works
    // prints: 1 | 2 | 3 | 4 | 5
    println(rgvw(v, " | "));
}

addrvw and pointervw

addrvw(p) prints the numeric value of a pointer in full-width hexadecimal with a 0x prefix — a classic "address" format. pointervw(p) is similar but aimed at generic pointer-like objects, giving a clean address-style output. Both work on raw pointers and pointer-like types.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    int x{42};
    int *p{&x};

    // prints: &x = 0x00007ffd...
    println("&x = ", addrvw(p));
    // prints: ptr = 0x00007ffd...
    println("ptr = ", pointervw(p));
}

hex_encode — Binary Data as Hex String

hex_encode(first, last) (or the range overload hex_encode(range)) turns a sequence of bytes into a hex string, which is perfect for printing hashes, keys, or raw buffers. hex_encode_upper produces uppercase letters.


#include <fast_io.h>
#include <fast_io_dsal/vector.h>

using namespace fast_io::io;

int main()
{
    ::fast_io::vector<::std::uint8_t> digest{
        0xDEz, 0xADz, 0xBEz, 0xEFz, 0x01z, 0x23z
    };
    // prints: deadbeef0123
    println(hex_encode(digest));
    // prints: DEADBEEF0123
    println(hex_encode_upper(digest));
}

ordinal — 1st, 2nd, 3rd…

ordinal(n) prints an integer with its English ordinal suffix: 1st, 2nd, 3rd, 4th, 21st, and so on. Useful for ranking, leaderboards, and dated log messages.


#include <fast_io.h>

using namespace fast_io::io;

int main()
{
    for (int i{1}; i <= 5; ++i)
    {
        // prints: 1st, 2nd, 3rd, 4th, 5th
        println(ordinal(i));
    }
}

Compound Manipulators: left_width, right_width

Compound manipulators combine multiple formatting operations into a single manipulator object. left_width and right_width are examples that combine value formatting with alignment. They are stateless — they apply only to the value they wrap, with no stream state to corrupt.

left_width(value, width, fill) formats value and left-aligns it within a field of the given width, padding with fill (defaults to space). Similarly, right_width(value, width, fill) right-aligns. These are useful when you want to combine formatting (like hex or fixed) with alignment in a single, composable expression.


#include <fast_io.h>

using namespace ::fast_io::iomnp;

int main()
{
    // Combine hex formatting with left alignment
    int value{255};
    println("|", left_width(hex(value), 10zu), "|");     // | ff        |
    println("|", right_width(hex(value), 10zu), "|");    // |        ff |

    // With custom fill character
    println("|", left_width(hex(value), 10zu, '0'), "|");   // | ff00000000|
    println("|", right_width(hex(value), 10zu, '*'), "|");  // | ********ff|

    // Combine with other manipulators
    double pi{3.14159};
    println("|", left_width(fixed(pi, 2zu), 15zu), "|");   // | 3.14         |
    println("|", right_width(fixed(pi, 2zu), 15zu), "|");  // |           3.14 |

    // These are stateless: each manipulator applies only to its value
    println(hex(255), " ", 255);  // prints: ff 255 (second value is decimal)
}

The key advantage is that these manipulators are stateless. There is no stream state to corrupt, no need to manually restore settings. Each manipulator is a self-contained object that applies only to the value it wraps.

Full Appendix

The manipulators shown above are only the most common ones. The library also provides base-N I/O (base<N>, base_get<N>), ISO timestamps, IP address formatting, hex floats, percentage formatters, transcode manipulators, and more.

For the complete reference, see the Appendix: Manipulators.

Key takeaways