Skip to content

Instantly share code, notes, and snippets.

@dk949
Last active February 25, 2025 10:50
Show Gist options
  • Save dk949/0b1d14bec6eebdc9c3270c73d4d2ad72 to your computer and use it in GitHub Desktop.
Save dk949/0b1d14bec6eebdc9c3270c73d4d2ad72 to your computer and use it in GitHub Desktop.
C++20 sensible print macros. Uses std::format (including vector formatting). See "Usage" at the top of the file.
#ifndef UT_PRINT_HPP
#define UT_PRINT_HPP
/* Usage:
```
// std >= c++20
auto number = 42;
ut_print("the number is {}", number);
```
output to stdout: the number is 42
`ut_eprint` does the same but writes to stderr
`ut_fprint` is the same but takes a FILE* as the first argument and writes to that
`ut_println`, `ut_eprintln` and `ut_fprintln` append a newline to the end of the
format string
```
#define UT_PRINT_PCVIEW
#include "print.hpp"
std::vector v{1,2,3,4};
ut_print("v is {}", ut::PCView(v));
```
output to stdout: v is [1, 2, 3, 4]
Should work on any container that implements the PrintableContainer concept.
Tested on `std::vector`, `std::array` and `std::list`.
```
// std >= c++23
#define UT_USE_STD
auto number = 42;
ut_print("the number is {}", number);
```
output to stdout: the number is 42
Printing is done using the std::print/std::println functions, which have a
chance of being more performant than std::puts(std::format).
```
#define UT_STD_ONLY
#include "print.hpp"
std::vector v{1,2,3,4};
std::print("v is {}", ut::PCView(v));
```
output to stdout: v is [1, 2, 3, 4]
This disables the ut_* macros and enables UT_PRINT_PCVIEW
Allows container printing with std::print
Note: Using std::format can significantly impact compile times. Here are the
times it took to compile a program formatting 10 different types using `printf`,
`std::cout` and `ut_println`.
Compile times (g++ -O3):
| printf | cout | ut_println(std::format) |
| ~0.1s | ~0.5s | ~3.5s |
License: see end of file
*/
#ifdef UT_STD_ONLY
# ifndef UT_USE_STD
# define UT_USE_STD
# endif
# ifndef UT_PRINT_PCVIEW
# define UT_PRINT_PCVIEW
# endif
#endif
#include <stdio.h>
#include <format>
#ifdef UT_USE_STD
# include <print>
#endif
#ifdef UT_PRINT_PCVIEW
# include <concepts>
# include <memory>
namespace ut {
template<typename C>
concept PrintableContainer = requires(C c) {
{ std::begin(c) } -> std::forward_iterator;
{ std::end(c) } -> std::sentinel_for<typename C::iterator>;
{ std::empty(c) } -> std::convertible_to<bool>;
std::format("{}", c.front());
}
&&std::same_as<std::remove_cvref_t<decltype(std::declval<C>().front())>, typename C::value_type>;
// Printable container view
template<PrintableContainer C>
struct PCView {
using value_type = C::value_type;
C const &view;
PCView(C const &v) noexcept
: view(v) { }
};
} // namespace ut
template<typename R, class CharT>
struct std::formatter<ut::PCView<R>, CharT> : public std::formatter<typename ut::PCView<R>::value_type, CharT> {
template<class FormatContext>
auto format(ut::PCView<R> vv, FormatContext &fc) const {
auto out = fc.out();
out = std::format_to(out, "[");
if (!std::empty(vv.view)) {
out = std::format_to(out, "{}", vv.view.front());
for (auto it = std::next(std::begin(vv.view)); it != std::end(vv.view); ++it) {
out = std::format_to(out, ", {}", *it);
}
}
return std::format_to(out, "]");
}
};
#endif
#ifndef UT_STD_ONLY
# ifdef UT_USE_STD
# define ut_fprint(file, fmt, ...) std::print(file, fmt __VA_OPT__(, ) __VA_ARGS__)
# define ut_fprintln(file, fmt, ...) std::println(file, fmt __VA_OPT__(, ) __VA_ARGS__)
# else
# define ut_fprint(file, fmt, ...) std::fputs(std::format(fmt __VA_OPT__(, ) __VA_ARGS__).c_str(), file)
# define ut_fprintln(file, fmt, ...) ut_fprint(file, fmt "\n", __VA_ARGS__)
# endif
# define ut_eprint(fmt, ...) ut_fprint(stderr, fmt __VA_OPT__(, ) __VA_ARGS__)
# define ut_eprintln(fmt, ...) ut_fprintln(stderr, fmt __VA_OPT__(, ) __VA_ARGS__)
# define ut_print(fmt, ...) ut_fprint(stdout, fmt __VA_OPT__(, ) __VA_ARGS__)
# define ut_println(fmt, ...) ut_fprintln(stdout, fmt __VA_OPT__(, ) __VA_ARGS__)
#endif // UT_STD_ONLY
/*
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <https://unlicense.org>
*/
#endif // UT_PRINT_HPP
macros for printing using `std::format` in c++20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment