microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
tuple.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
16#include "../microfmt.hpp"
17#include <array>
18#include <cstddef>
19#include <string_view>
20#include <tuple>
21#include <type_traits>
22#include <utility>
23
24namespace microfmt {
25
26namespace detail {
27
28template <typename T> using remove_cvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
29
30template <typename T, typename = void> struct has_data : std::false_type {};
31
32template <typename T> struct has_data<T, std::void_t<decltype(std::declval<T &>().data())>> : std::true_type {};
33
39template <typename T, typename = void> struct is_tuple_like : std::false_type {};
40
41template <typename T>
42struct is_tuple_like<T, std::void_t<decltype(std::tuple_size<remove_cvref_t<T>>::value)>>
43 : std::integral_constant<bool, !has_data<remove_cvref_t<T>>::value> {};
44
45template <typename Tuple, size_t... Is>
46void format_tuple_impl(const Tuple &t, const sink &out, microfmt::string_view sep, microfmt::string_view spec,
47 std::index_sequence<Is...>) noexcept {
48 size_t idx = 0;
49 auto format_elem = [&idx, &out, &sep, &spec](const auto &elem) noexcept {
50 if (idx++ > 0) {
51 out.write(sep);
52 }
53 using ElemType = remove_cvref_t<decltype(elem)>;
55 format_parse_context ctx(spec);
56 f.parse(ctx);
57 f.format(elem, out);
58 };
59
60 (format_elem(std::get<Is>(t)), ...);
61}
62
63} // namespace detail
64
71template <typename T> struct formatter<T, std::enable_if_t<detail::is_tuple_like<T>::value>> {
72 char open_delim{'('};
73 char close_delim{')'};
74 microfmt::string_view separator{", "};
75 microfmt::string_view forwarded_spec{""};
76
77 constexpr void parse(format_parse_context &ctx) noexcept {
78 auto spec = ctx.spec();
79 if (spec.empty())
80 return;
81
82 size_t i = 0;
83 // Check custom delimiter style
84 if (spec[0] == 'b' || spec[0] == 'B') { // Bracket style: [a, b, c]
85 open_delim = '[';
86 close_delim = ']';
87 i = 1;
88 } else if (spec[0] == 'c' || spec[0] == 'C') { // Curly/Brace style: {a, b, c}
89 open_delim = '{';
90 close_delim = '}';
91 i = 1;
92 } else if (spec[0] == 'n' || spec[0] == 'N') { // Naked/Bare: a, b, c (no delimiters)
93 open_delim = '\0';
94 close_delim = '\0';
95 i = 1;
96 } else if (spec[0] == 'p' || spec[0] == 'P') { // Standard parens: (a, b, c)
97 open_delim = '(';
98 close_delim = ')';
99 i = 1;
100 }
101
102 forwarded_spec = spec.substr(i);
103 }
104
105 void format(const T &t, const sink &out) const noexcept {
106 if (open_delim != '\0') {
107 out.put(open_delim);
108 }
109
110 constexpr size_t N = std::tuple_size<detail::remove_cvref_t<T>>::value;
111 if constexpr (N > 0) {
112 detail::format_tuple_impl(t, out, separator, forwarded_spec, std::make_index_sequence<N>{});
113 }
114
115 if (close_delim != '\0') {
116 out.put(close_delim);
117 }
118 }
119};
120
121} // namespace microfmt
Definition microfmt.hpp:881
typename std::remove_cv< typename std::remove_reference< T >::type >::type remove_cvref_t
Definition tuple.hpp:28
void format_tuple_impl(const Tuple &t, const sink &out, microfmt::string_view sep, microfmt::string_view spec, std::index_sequence< Is... >) noexcept
Definition tuple.hpp:46
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
Drop-in replacement for a formatter<T> underlying_formatter; member used by container/wrapper formatt...
Definition microfmt.hpp:1433
Definition tuple.hpp:30
Definition tuple.hpp:39
void format(const T &t, const sink &out) const noexcept
Definition tuple.hpp:105
constexpr void parse(format_parse_context &ctx) noexcept
Definition tuple.hpp:77
Definition microfmt.hpp:985