Print
The print API provides convenient output helpers for Vix applications.
It is designed to make C++ output simple, readable, and useful during development.
#include <vix/print.hpp>
int main()
{
vix::print("Hello from Vix");
vix::print(42, true, std::vector<int>{1, 2, 3});
return 0;
}Header
#include <vix/print.hpp>Some examples may include vix.hpp, which can expose the common Vix APIs:
#include <vix.hpp>For direct usage of the print API, prefer:
#include <vix/print.hpp>What vix::print does
vix::print writes one or more values to an output stream.
By default:
- values are separated by a space,
- a newline is added at the end,
- output goes to
std::cout, - strings are printed as raw strings,
- containers are rendered in a readable form,
- common STL types are handled automatically.
vix::print("Hello", "Vix", 2026);Output:
Hello Vix 2026Basic usage
#include <vix/print.hpp>
int main()
{
vix::print("Hello from Vix");
vix::print("version", 1);
vix::print(true, false);
vix::print(3.14);
return 0;
}Output shape:
Hello from Vix
version 1
true false
3.14Print a blank line
Calling vix::print() with no arguments prints a blank line.
vix::print("Before");
vix::print();
vix::print("After");Output:
Before
AfterMultiple values
vix::print("user", 42, "active", true);Output:
user 42 active trueBy default, arguments are separated by one space.
vix::print vs std::cout
With std::cout, you write:
std::cout << "user " << id << " active " << active << "\n";With vix::print, you write:
vix::print("user", id, "active", active);vix::print is useful when you want concise output and automatic rendering for STL containers and common C++ types.
Public API overview
| API | Purpose |
|---|---|
vix::print(...) | Print values to the default output stream. |
vix::print(config, ...) | Print values using a print_config. |
vix::print(options, ...) | Print values using lightweight options. |
vix::print_py(...) | Print with Python-like raw string behavior. |
vix::print_to(os, ...) | Print values to a specific stream. |
vix::eprint(...) | Print values to std::cerr. |
vix::print_inline(...) | Print without a trailing newline. |
vix::sprint(...) | Format values into a std::string. |
vix::to_string(value) | Format one value into a std::string. |
vix::write_to(os, value) | Write one value to a stream. |
vix::print_named(label, value) | Print label: value. |
vix::print_separator() | Print a separator line. |
vix::print_header(title) | Print a titled section header. |
vix::print_each(range) | Print each range element on its own line. |
vix::print_table(map) | Print a map-like container as a table. |
vix::pprint(value) | Pretty-print nested structures on multiple lines. |
vix::pprint_all(...) | Pretty-print multiple values. |
vix::print_stats(range) | Print min, max, sum, and average for numeric ranges. |
vix::rendering_path<T>() | Return how a type will be rendered. |
vix::print_rendering_paths<Ts...>() | Print rendering paths for several types. |
print_config
print_config controls how vix::print renders values.
vix::print_config cfg;
cfg.separator = " | ";
cfg.end = "\n";
cfg.out = &std::cout;
cfg.max_items = 256;
cfg.raw_strings = true;
vix::print(cfg, "A", "B", "C");Output:
A | B | CFields
| Field | Default | Purpose |
|---|---|---|
separator | " " | Text between multiple arguments. |
end | "\n" | Text printed after all arguments. |
out | &std::cout | Destination stream. |
color | false | ANSI color support, experimental. |
max_items | 256 | Maximum items rendered in a container. |
show_type | false | Type annotations, currently reserved. |
compact | false | Compact rendering mode, mainly for pretty internals. |
indent_str | " " | Indentation unit. |
raw_strings | true | Print strings without quotes by default. |
options
options is a lightweight way to configure one print call.
vix::print(
vix::options{
.sep = ", ",
.end = ";\n"
},
1, 2, 3
);Output:
1, 2, 3;Fields
| Field | Default | Purpose |
|---|---|---|
sep | " " | Text between arguments. |
end | "\n" | Text after the arguments. |
file | &std::cout | Output stream. |
flush | false | Flush the stream after printing. |
raw_strings | true | Print strings without quotes. |
max_items | 256 | Maximum container items. |
compact | true | Compact output. |
indent | " " | Indentation unit. |
show_type | false | Type annotations, currently reserved. |
color | false | ANSI color support, experimental. |
Custom separator
vix::print(vix::options{.sep = ", "}, "red", "green", "blue");Output:
red, green, blueCustom line ending
vix::print(vix::options{.end = ""}, "loading");
vix::print(" done");Output shape:
loading doneFor this specific case, you can also use print_inline.
Print without newline
vix::print_inline("Loading...");
vix::print(" done");Output shape:
Loading... donePrint to stderr
Use eprint for errors.
vix::eprint("error:", "failed to open file");This writes to std::cerr.
Print to another stream
Use print_to.
#include <fstream>
#include <vix/print.hpp>
int main()
{
std::ofstream file("output.txt");
vix::print_to(file, "Hello", "file");
return 0;
}Format to string with sprint
sprint returns a formatted string without writing to stdout.
std::string message = vix::sprint("user", 42, "active", true);
vix::print(message);Output:
user 42 active trueFormat one value with to_string
Use to_string when you only need one value.
std::string s = vix::to_string(std::vector<int>{1, 2, 3});
vix::print(s);Output:
[1, 2, 3]Write one value to a stream
Use write_to when you want the rendering engine but not the full print behavior.
std::ostringstream out;
vix::write_to(out, std::vector<int>{1, 2, 3});
std::string s = out.str();Unlike print, write_to does not add a separator or newline.
Named values
vix::print_named("port", 8080);
vix::print_named("mode", "development");Output:
port: 8080
mode: developmentSeparators and headers
vix::print_separator();
vix::print_header("Users");Example:
vix::print_header("Startup");
vix::print("server ready");
vix::print_separator();Output shape:
------------------------------------------------------------
Startup
------------------------------------------------------------
server ready
------------------------------------------------------------You can customize the width:
vix::print_separator(30);
vix::print_header("Config", 30);Print each element of a range
std::vector<std::string> names = {"Ada", "Bjarne", "Linus"};
vix::print_each(names);Output:
Ada
Bjarne
LinusWith indexes:
vix::print_each(names, true);Output:
[0] Ada
[1] Bjarne
[2] LinusPrint a map as a table
std::map<std::string, int> values = {
{"users", 120},
{"orders", 42},
{"errors", 3}
};
vix::print_table(values);Output shape:
-------------------------------------------
errors | 3
orders | 42
users | 120
-------------------------------------------Custom column width:
vix::print_table(values, 12);Pretty printing with pprint
print renders most values on one line.
std::map<std::string, std::vector<int>> data = {
{"evens", {2, 4, 6}},
{"odds", {1, 3, 5}}
};
vix::print(data);Output shape:
{evens => [2, 4, 6], odds => [1, 3, 5]}Use pprint for multi-line nested output.
vix::pprint(data);Output shape:
{
evens => [
2
4
6
]
odds => [
1
3
5
]
}Pretty-print multiple values
vix::pprint_all(
std::vector<int>{1, 2, 3},
std::map<std::string, int>{{"a", 1}, {"b", 2}}
);Each value is printed using the pretty-printer.
Print numeric statistics
print_stats works with ranges of arithmetic values.
std::vector<int> values = {3, 7, 2, 10};
vix::print_stats(values, "scores");Output shape:
scores: stats(min=2, max=10, sum=22, avg=5.5)For an empty range:
std::vector<int> values;
vix::print_stats(values, "scores");Output:
scores: stats(empty)Supported types
The print engine supports many common C++ types automatically.
| Type category | Examples |
|---|---|
| Booleans | bool |
| Characters | char, wchar_t, char8_t, char16_t, char32_t |
| Strings | std::string, std::string_view, const char * |
| Wide strings | std::wstring, std::wstring_view, const wchar_t * |
| Numbers | int, double, std::size_t, etc. |
| Null | nullptr |
| Enums | enum, enum class |
| Filesystem | std::filesystem::path |
| Optional | std::optional<T> |
| Variant | std::variant<Ts...> |
| Any | std::any |
| Reference wrapper | std::reference_wrapper<T> |
| Chrono duration | std::chrono::seconds, milliseconds, etc. |
| Chrono time point | std::chrono::time_point |
| Smart pointers | std::unique_ptr, std::shared_ptr, std::weak_ptr |
| Raw pointers | T * |
| Function pointers | function pointer values |
| Ranges | std::vector, std::list, std::deque, etc. |
| Sets | std::set, std::unordered_set, etc. |
| Maps | std::map, std::unordered_map, etc. |
| Container adapters | std::stack, std::queue, std::priority_queue |
| Tuple-like values | std::tuple, std::pair, std::array |
| Error types | std::error_code, std::error_condition |
| Byte | std::byte |
| Monostate | std::monostate |
| Streamable types | Any type with operator<< |
| Custom formatted types | vix::formatter<T> or vix_format |
If C++23 std::expected is available, std::expected<T, E> is also supported.
Strings
By default, strings are printed raw.
vix::print("hello");
vix::print(std::string{"world"});Output:
hello
worldIf you want quoted strings, use raw_strings = false.
vix::print(vix::options{.raw_strings = false}, "hello");Output:
"hello"Booleans
vix::print(true, false);Output:
true falseCharacters
vix::print('A');Output:
'A'Wide and Unicode character types are rendered using escaped forms when needed.
Null values
vix::print(nullptr);Output:
nullptrVectors and sequences
vix::print(std::vector<int>{1, 2, 3});
vix::print(std::list<std::string>{"a", "b", "c"});Output shape:
[1, 2, 3]
[a, b, c]Sets
vix::print(std::set<int>{1, 2, 3});Output shape:
[1, 2, 3]Sets are rendered like ranges.
Maps
std::map<std::string, int> ages = {
{"Ada", 36},
{"Bjarne", 75}
};
vix::print(ages);Output shape:
{Ada => 36, Bjarne => 75}Pairs
vix::print(std::make_pair("id", 42));Output shape:
(id: 42)Tuples
auto value = std::make_tuple("user", 42, true);
vix::print(value);Output shape:
(user, 42, true)Optionals
std::optional<int> a = 42;
std::optional<int> b;
vix::print(a);
vix::print(b);Output:
Some(42)
NoneVariants
std::variant<int, std::string> value = std::string{"hello"};
vix::print(value);Output shape:
variant<hello>Any
std::any a = 42;
std::any empty;
vix::print(a);
vix::print(empty);Output shape:
any(i)
any(empty)std::any prints the stored type name, not the stored value.
Chrono durations
using namespace std::chrono;
vix::print(42ns);
vix::print(100us);
vix::print(1500ms);
vix::print(60s);
vix::print(2min);
vix::print(1h);Output shape:
42ns
100µs
1500ms
60s
2min
1hOn Windows, microseconds may be rendered as us.
Chrono time points
auto now = std::chrono::system_clock::now();
vix::print(now);Output shape:
<time_point: 1710000000000ms since epoch>The exact value depends on the clock and current time.
Filesystem paths
std::filesystem::path path = "/usr/local/include";
vix::print(path);Output:
path("/usr/local/include")Smart pointers
auto value = std::make_unique<int>(42);
std::unique_ptr<int> empty;
vix::print(value);
vix::print(empty);Output:
unique_ptr(42)
unique_ptr(null)Shared pointers include the use count:
auto shared = std::make_shared<std::string>("hello");
vix::print(shared);Output shape:
shared_ptr[use=1](hello)Weak pointers show whether they are expired or lockable:
std::weak_ptr<std::string> weak = shared;
vix::print(weak);Output shape:
weak_ptr[use=2](hello)Raw pointers
int value = 42;
int *ptr = &value;
int *empty = nullptr;
vix::print(ptr);
vix::print(empty);Output shape:
<ptr:0x...>
nullptrPointers print their address, not the pointed value.
Function pointers
void (*fn)(int) = [](int) {};
vix::print(fn);Output shape:
<fptr:0x...>Container adapters
Stack
std::stack<int> values;
values.push(1);
values.push(2);
values.push(3);
vix::print(values);Output shape:
stack[1, 2, 3]Queue
std::queue<std::string> q;
q.push("first");
q.push("second");
vix::print(q);Output shape:
queue[first, second]Priority queue
std::priority_queue<int> pq;
pq.push(5);
pq.push(1);
pq.push(4);
vix::print(pq);Output shape:
priority_queue[5, 4, 1]Container adapters are copied internally before printing, so printing does not mutate the original container.
Limit container output
Use max_items to avoid printing very large containers.
std::vector<int> values = {1, 2, 3, 4, 5};
vix::print(vix::options{.max_items = 3}, values);Output shape:
[1, 2, 3, ...]Default configuration
default_config() returns a thread-local default configuration.
auto &cfg = vix::default_config();
cfg.separator = " | ";
cfg.end = "\n";
cfg.max_items = 10;
vix::print(1, 2, 3);Output:
1 | 2 | 3Because the default config is thread-local, changing it affects the current thread.
Scoped configuration
Use scoped_config to temporarily override the default print configuration.
vix::print(1, 2, 3);
{
vix::scoped_config guard{};
guard.cfg.separator = ", ";
guard.cfg.end = ";\n";
vix::print(1, 2, 3);
}
vix::print(1, 2, 3);Output:
1 2 3
1, 2, 3;
1 2 3When the guard is destroyed, the previous config is restored.
Custom type support
There are three common ways to make a custom type printable.
- Define
operator<< - Define an ADL
vix_formathook - Specialize
vix::formatter<T>
Method 1: use operator<<
#include <ostream>
#include <string>
#include <vix/print.hpp>
struct User
{
int id{};
std::string name;
};
std::ostream &operator<<(std::ostream &os, const User &user)
{
return os << "User{id=" << user.id << ", name=" << user.name << "}";
}
int main()
{
User user{1, "Ada"};
vix::print(user);
return 0;
}Output:
User{id=1, name=Ada}Use this when the stream representation is useful everywhere, not only in Vix.
Method 2: use ADL vix_format
Define vix_format in the same namespace as your type.
#include <ostream>
#include <vix/print.hpp>
namespace app
{
struct Point
{
double x{};
double y{};
};
void vix_format(std::ostream &os, const Point &point)
{
os << "Point{x=" << point.x << ", y=" << point.y << "}";
}
}
int main()
{
app::Point p{1.5, 2.5};
vix::print(p);
return 0;
}Output:
Point{x=1.5, y=2.5}Use this when you want Vix-specific formatting without defining a global stream operator.
Method 3: specialize vix::formatter<T>
#include <ostream>
#include <string>
#include <vix/print.hpp>
struct Color
{
unsigned char r{};
unsigned char g{};
unsigned char b{};
unsigned char a{255};
};
template <>
struct vix::formatter<Color>
{
static void format(std::ostream &os, const Color &color)
{
os << "Color("
<< "r=" << static_cast<int>(color.r)
<< ", g=" << static_cast<int>(color.g)
<< ", b=" << static_cast<int>(color.b)
<< ", a=" << static_cast<int>(color.a)
<< ")";
}
};
int main()
{
Color red{255, 0, 0, 255};
vix::print(red);
return 0;
}Output:
Color(r=255, g=0, b=0, a=255)Use this when you want full control over how Vix prints a type.
streamable_formatter
If your type already has operator<<, you can delegate to it:
template <>
struct vix::formatter<MyType> : vix::streamable_formatter<MyType>
{
};This is useful when you want an explicit vix::formatter specialization but still want to reuse stream output.
Rendering priority
When Vix prints a value, it chooses the first matching renderer.
The priority is:
vix::formatter<T>specialization- ADL
vix_format nullptr_tbool- character types
- wide strings
- string-like types
std::filesystem::pathstd::anystd::optional<T>std::variant<Ts...>std::reference_wrapper<T>- chrono duration
- chrono time point
- smart pointers
- container adapters
std::expected<T, E>, when available- enum types
- map-like ranges
- sequence and set ranges
- pair
- tuple-like types
- raw pointers
- function pointers
- streamable types using
operator<< - fallback unprintable type name
This matters when a type can match more than one category.
Inspect the rendering path
Use rendering_path<T>() to understand which renderer Vix will use.
vix::print(vix::rendering_path<int>());
vix::print(vix::rendering_path<std::vector<int>>());
vix::print(vix::rendering_path<std::map<std::string, int>>());Output shape:
bool
range
map-like rangeYou can also print several paths at once:
vix::print_rendering_paths<
int,
std::string,
std::vector<int>,
std::map<std::string, int>,
std::optional<int>
>();Complete example
#include <map>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include <vix/print.hpp>
struct Point
{
int x{};
int y{};
};
void vix_format(std::ostream &os, const Point &point)
{
os << "Point{x=" << point.x << ", y=" << point.y << "}";
}
int main()
{
vix::print_header("Vix print");
vix::print("hello", 42, true);
vix::print(std::vector<int>{1, 2, 3});
vix::print(std::map<std::string, int>{
{"users", 120},
{"orders", 42}
});
vix::print(std::optional<int>{42});
vix::print(std::optional<int>{});
std::variant<int, std::string> value = std::string{"active"};
vix::print(value);
vix::print(Point{10, 20});
std::string message = vix::sprint("status", "ok");
vix::print_named("message", message);
vix::print_each(std::vector<std::string>{"alpha", "beta"}, true);
vix::print_table(std::map<std::string, int>{
{"a", 1},
{"b", 2}
});
return 0;
}Use in Vix apps
vix::print is useful in simple scripts, examples, demos, and local development.
#include <vix.hpp>
#include <vix/print.hpp>
using namespace vix;
int main()
{
App app;
app.get("/health", [](Request &, Response &res) {
vix::print("health route called");
res.json({
"ok", true,
"service", "api"
});
});
app.run(8080);
return 0;
}For production application logs, prefer the Vix logging API:
#include <vix/log.hpp>
vix::log::info("server started");
vix::log::error("database connection failed");Use print for direct output.
Use log for structured production logging.
Common mistakes
Forgetting the header
// Wrong
vix::print("Hello");Fix:
#include <vix/print.hpp>Expecting strings to be quoted by default
By default, strings are raw:
vix::print("hello");Output:
helloUse raw_strings = false if you want quotes:
vix::print(vix::options{.raw_strings = false}, "hello");Output:
"hello"Printing huge containers
std::vector<int> values(1'000'000);
vix::print(values);Better:
vix::print(vix::options{.max_items = 20}, values);Using print for production logs
This works:
vix::print("user created");But for production logs, prefer:
vix::log::info("user created");Expecting std::any to print the stored value
std::any value = 42;
vix::print(value);Output shape:
any(i)std::any prints the stored type name, not the stored value.
Expecting raw pointers to dereference
int value = 42;
int *ptr = &value;
vix::print(ptr);Output shape:
<ptr:0x...>Raw pointers print addresses.
Smart pointers print the contained value when non-null.
Best practices
Use vix::print for quick readable output:
vix::print("server ready", 8080);Use vix::sprint when you need a string:
std::string label = vix::sprint("user", id);Use vix::eprint for errors in command-line tools:
vix::eprint("error:", "missing input file");Use print_each for lists:
vix::print_each(items, true);Use print_table for maps:
vix::print_table(config);Use pprint for nested structures:
vix::pprint(data);Use formatter<T> or vix_format for custom domain types:
vix::print(User{1, "Ada"});API reference
print
template <typename... Args>
void print(const Args &...args);Prints values using the thread-local default config.
vix::print("hello", 42);print with config
template <typename... Args>
void print(const print_config &cfg, const Args &...args);Prints values using an explicit print_config.
vix::print_config cfg;
cfg.separator = ", ";
vix::print(cfg, 1, 2, 3);print with options
template <typename... Args>
void print(const options &opts, const Args &...args);Prints values using lightweight options.
vix::print(vix::options{.sep = ", "}, 1, 2, 3);print_py
template <typename... Args>
void print_py(const Args &...args);Prints with raw string behavior enabled.
vix::print_py("hello", "world");print_to
template <typename... Args>
void print_to(std::ostream &os, const Args &...args);Prints to a specific stream.
std::ostringstream out;
vix::print_to(out, "hello");eprint
template <typename... Args>
void eprint(const Args &...args);Prints to std::cerr.
vix::eprint("error:", "not found");print_inline
template <typename... Args>
void print_inline(const Args &...args);Prints without a trailing newline.
vix::print_inline("Loading...");sprint
template <typename... Args>
std::string sprint(const Args &...args);Formats values into a string.
std::string s = vix::sprint("id", 42);to_string
template <typename T>
std::string to_string(const T &value);Formats one value into a string.
std::string s = vix::to_string(std::vector<int>{1, 2, 3});write_to
template <typename T>
void write_to(std::ostream &os, const T &value);Writes one rendered value to a stream.
vix::write_to(std::cout, std::vector<int>{1, 2, 3});print_named
template <typename T>
void print_named(std::string_view label, const T &value);Prints a label and a value.
vix::print_named("port", 8080);print_separator
void print_separator(std::size_t width = 60, char ch = '-');Prints a separator line.
vix::print_separator();
vix::print_separator(30, '=');print_header
void print_header(std::string_view title, std::size_t width = 60);Prints a section header.
vix::print_header("Startup");print_each
template <typename Range>
void print_each(const Range &rng, bool show_index = false);Prints each element of a range on its own line.
vix::print_each(std::vector<std::string>{"a", "b"}, true);print_table
template <typename Map>
void print_table(const Map &m, std::size_t col_width = 20);Prints a map-like container as a two-column table.
vix::print_table(std::map<std::string, int>{{"a", 1}, {"b", 2}});pprint
template <typename T>
void pprint(const T &value, const print_config &cfg = default_config());Pretty-prints a value using multi-line indentation.
vix::pprint(std::map<std::string, std::vector<int>>{
{"a", {1, 2}},
{"b", {3, 4}}
});pprint_all
template <typename... Args>
void pprint_all(const Args &...args);Pretty-prints several values.
vix::pprint_all(std::vector<int>{1, 2}, std::map<std::string, int>{{"x", 1}});print_stats
template <typename Range>
void print_stats(const Range &rng, std::string_view label = "");Prints statistics for a numeric range.
vix::print_stats(std::vector<int>{1, 2, 3}, "values");default_config
print_config &default_config() noexcept;Returns the thread-local print configuration.
vix::default_config().separator = " | ";to_print_config
print_config to_print_config(const options &opts);Converts options into print_config.
vix::print_config cfg = vix::to_print_config(vix::options{.sep = ", "});scoped_config
class scoped_config;Temporarily overrides the default config and restores it automatically.
{
vix::scoped_config guard{};
guard.cfg.separator = ", ";
vix::print(1, 2, 3);
}formatter
template <typename T, typename = void>
struct formatter;Specialize this template to define custom Vix rendering.
template <>
struct vix::formatter<MyType>
{
static void format(std::ostream &os, const MyType &value)
{
os << "...";
}
};streamable_formatter
template <typename T>
struct streamable_formatter;Helper formatter that delegates to operator<<.
template <>
struct vix::formatter<MyType> : vix::streamable_formatter<MyType>
{
};rendering_path
template <typename T>
std::string_view rendering_path() noexcept;Returns the selected rendering strategy for a type.
vix::print(vix::rendering_path<std::vector<int>>());print_rendering_paths
template <typename... Ts>
void print_rendering_paths();Prints rendering strategies for several types.
vix::print_rendering_paths<int, std::string, std::vector<int>>();Summary
vix::print is the simple output API for Vix.
Use it for:
- examples,
- demos,
- local debugging,
- CLI tools,
- quick scripts,
- readable STL output,
- custom type formatting.
Remember the core tools:
vix::print(...); // print values
vix::eprint(...); // print to stderr
vix::print_inline(...); // no newline
vix::sprint(...); // return string
vix::print_each(...); // one item per line
vix::print_table(...); // map-like table
vix::pprint(...); // multi-line nested outputFor production logs, use vix::log.
For direct developer output, use vix::print.