microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
map_view.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
17#include "../microfmt.hpp"
18#include <cstddef>
19#include <string_view>
20#include <type_traits>
21#include <utility>
22
23namespace microfmt {
24
26template <typename KeyExtractor, typename ValExtractor, typename Iterator, typename Sentinel>
27struct RELOCO_POINTER map_range_view {
28 Iterator first;
29 Sentinel last;
30 KeyExtractor key_fn;
31 ValExtractor val_fn;
32 microfmt::string_view open_delim{"{"};
33 microfmt::string_view close_delim{"}"};
34 microfmt::string_view sep{", "};
35 microfmt::string_view kv_sep{": "};
36};
37
38namespace detail {
39
40template <typename T> using remove_cvref_map_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
41
42template <typename T, typename = void> struct has_first_member : std::false_type {};
43
44template <typename T>
45struct has_first_member<T, std::void_t<decltype(std::declval<const T &>().first)>> : std::true_type {};
46
47template <typename T, typename = void> struct has_second_member : std::false_type {};
48
49template <typename T>
50struct has_second_member<T, std::void_t<decltype(std::declval<const T &>().second)>> : std::true_type {};
51
52template <typename T, typename = void> struct has_key_method : std::false_type {};
53
54template <typename T>
55struct has_key_method<T, std::void_t<decltype(std::declval<const T &>().key())>> : std::true_type {};
56
57template <typename T, typename = void> struct has_value_method : std::false_type {};
58
59template <typename T>
60struct has_value_method<T, std::void_t<decltype(std::declval<const T &>().value())>> : std::true_type {};
61
62template <typename T, typename = void> struct is_dereferenceable : std::false_type {};
63
64template <typename T> struct is_dereferenceable<T, std::void_t<decltype(*std::declval<T &>())>> : std::true_type {};
65
66} // namespace detail
67
70 template <typename T> constexpr decltype(auto) operator()(const T &item) const noexcept {
72 return (item.first);
73 } else if constexpr (detail::has_key_method<T>::value) {
74 return item.key();
75 } else {
76 return (item);
77 }
78 }
79};
80
83 template <typename T> constexpr decltype(auto) operator()(const T &item) const noexcept {
85 return (item.second);
86 } else if constexpr (detail::has_value_method<T>::value) {
87 return item.value();
88 } else {
89 return (item);
90 }
91 }
92};
93
95template <typename MapContainer> [[nodiscard]] constexpr auto map_view(const MapContainer &m) noexcept {
96 return map_range_view<default_key_fn, default_val_fn, decltype(m.begin()), decltype(m.end())>{
97 m.begin(), m.end(), default_key_fn{}, default_val_fn{}};
98}
99
100template <typename MapContainer, std::enable_if_t<!std::is_lvalue_reference_v<MapContainer>, int> = 0>
101[[nodiscard]] constexpr auto map_view(MapContainer &&) noexcept = delete;
102
104template <typename MapContainer, typename KeyFn, typename ValFn>
105[[nodiscard]] constexpr auto map_view(const MapContainer &m, KeyFn &&kfn, ValFn &&vfn) noexcept {
106 return map_range_view<std::decay_t<KeyFn>, std::decay_t<ValFn>, decltype(m.begin()), decltype(m.end())>{
107 m.begin(), m.end(), std::forward<KeyFn>(kfn), std::forward<ValFn>(vfn)};
108}
109
110template <typename MapContainer, typename KeyFn, typename ValFn,
111 std::enable_if_t<!std::is_lvalue_reference_v<MapContainer>, int> = 0>
112[[nodiscard]] constexpr auto map_view(MapContainer &&, KeyFn &&, ValFn &&) noexcept = delete;
113
115template <typename Iterator, typename Sentinel, typename KeyFn = default_key_fn, typename ValFn = default_val_fn,
116 typename std::enable_if<detail::is_dereferenceable<Iterator>::value, int>::type = 0>
117[[nodiscard]] constexpr auto map_view(Iterator first, Sentinel last, KeyFn &&kfn = KeyFn{},
118 ValFn &&vfn = ValFn{}) noexcept {
119 return map_range_view<std::decay_t<KeyFn>, std::decay_t<ValFn>, Iterator, Sentinel>{
120 first, last, std::forward<KeyFn>(kfn), std::forward<ValFn>(vfn)};
121}
122
129template <typename KeyExtractor, typename ValExtractor, typename Iterator, typename Sentinel>
130struct formatter<map_range_view<KeyExtractor, ValExtractor, Iterator, Sentinel>> {
131 char open_c{'{'};
132 char close_c{'}'};
133 microfmt::string_view forwarded_spec{""};
134
135 constexpr void parse(format_parse_context &ctx) noexcept {
136 auto spec = ctx.spec();
137 if (spec.empty())
138 return;
139
140 size_t i = 0;
141 if (spec[0] == 'b' || spec[0] == 'B') { // Bracketed: [k: v, ...]
142 open_c = '[';
143 close_c = ']';
144 i = 1;
145 } else if (spec[0] == 'n' || spec[0] == 'N') { // Naked: k: v, ...
146 open_c = '\0';
147 close_c = '\0';
148 i = 1;
149 } else if (spec[0] == 'c' || spec[0] == 'C') { // Curly: {k: v, ...}
150 open_c = '{';
151 close_c = '}';
152 i = 1;
153 }
154
155 forwarded_spec = spec.substr(i);
156 }
157
159 const sink &out) const noexcept {
160 if (open_c != '\0') {
161 out.put(open_c);
162 }
163
164 size_t idx = 0;
165 RELOCO_BEGIN_UNSAFE_BUFFER_USAGE;
166
167 for (auto it = view.first; it != view.last; ++it) {
168 if (idx++ > 0) {
169 out.write(view.sep);
170 }
171
172 const auto &k = view.key_fn(*it);
173 const auto &v = view.val_fn(*it);
174
175 using KeyType = detail::remove_cvref_map_t<decltype(k)>;
176 using ValType = detail::remove_cvref_map_t<decltype(v)>;
177
180
181 format_parse_context kctx(forwarded_spec);
182 key_fmt.parse(kctx);
183 key_fmt.format(k, out);
184
185 out.write(view.kv_sep);
186
187 format_parse_context vctx(forwarded_spec);
188 val_fmt.parse(vctx);
189 val_fmt.format(v, out);
190 }
191
192 if (close_c != '\0') {
193 out.put(close_c);
194 }
195
196 RELOCO_END_UNSAFE_BUFFER_USAGE;
197 }
198};
199
200} // namespace microfmt
Definition microfmt.hpp:881
#define MICROFMT_API_CLASS
Definition compat.hpp:181
typename std::remove_cv< typename std::remove_reference< T >::type >::type remove_cvref_map_t
Definition map_view.hpp:40
Definition assert.hpp:19
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
constexpr auto map_view(const MapContainer &m) noexcept
Definition map_view.hpp:95
Definition map_view.hpp:69
Definition map_view.hpp:82
Drop-in replacement for a formatter<T> underlying_formatter; member used by container/wrapper formatt...
Definition microfmt.hpp:1433
constexpr void parse(format_parse_context &ctx) noexcept
Definition microfmt.hpp:1445
void format(const T &value, const sink &out) const noexcept
Definition microfmt.hpp:1447
Definition map_view.hpp:42
Definition map_view.hpp:52
Definition map_view.hpp:47
Definition map_view.hpp:57
Definition map_view.hpp:62
constexpr void parse(format_parse_context &ctx) noexcept
Definition map_view.hpp:135
void format(const map_range_view< KeyExtractor, ValExtractor, Iterator, Sentinel > &view, const sink &out) const noexcept
Definition map_view.hpp:158
Definition microfmt.hpp:985
Definition map_view.hpp:27
Sentinel last
Definition map_view.hpp:29
Iterator first
Definition map_view.hpp:28
KeyExtractor key_fn
Definition map_view.hpp:30
ValExtractor val_fn
Definition map_view.hpp:31